jndi

原理:
         在DataSource中事先创建多个数据库链接,保存在数据库链接池中。当程序访问数据库时,只用从链接池中取空闲状态的数据库链接便可,访问结束,销毁资源,数据库链接从新回到链接池,这与每次去直接访问数据库相比,会节省大量时间和资源。
         JNDI( Java Naming and Directory Interface ),是Java平台的一个标准扩展,提供了一组接口、类和关于命名空间的概念。如同其它不少Java技术同样,JDNI是provider-based的技术,暴露了一个 API和一个服务供应接口(SPI)。这意味着任何基于名字的技术都能经过JNDI而提供服务,只要JNDI支持这项技术。JNDI目前所支持的技术包括 LDAP、CORBA Common Object Service(COS)名字服务、RMI、NDS、DNS、Windows注册表等等。不少J2EE技术,包括EJB都依靠JNDI来组织和定位实体。能够把它理解为一种将对象和名字捆绑的技术,对象工厂负责生产出对象,这些对象都和惟一的名字绑在一块儿,外部资源能够经过名字得到某对象的引用。html

          在javax.naming的包包中提供Context接口,提供了两个很好用的方法:
<1> void bind( String name , Object object )
        将名称绑定到对象。全部中间上下文和目标上下文(由该名称最终原子组件之外的其余全部组件指定)都必须已经存在。 
<2>Object lookup( String name ) 
       检索指定的对象。若是 name为空,则返回此上下文的一个新实例(该实例表示与此上下文相同的命名上下文,但其环境能够独立地进行修改,并且能够并发访问)。java

运行机制: 
一、 首先程序代码获取初始化的 JNDI 环境而且调用 Context.lookup() 方法从 JNDI 服务提供者那里获一个 DataSource 对象mysql

二、 中间层 JNDI 服务提供者返回一个 DataSource 对象给当前的 Java 应用程序这个 DataSource 对象表明了中间层服务上现存的缓冲数据源web

 三、 应用程序调用 DataSource 对象的 getConnection() 方法sql

四、 当 DataSource 对象的 getConnection() 方法被调用时,中间层服务器将查询数据库 链接缓冲池中有没有 PooledConnection 接口的实例对象。这个 PooledConnection 对象将被用于与数据库创建物理上的数据库链接数据库

五、 若是在缓冲池中命中了一个 PooledCoonection 对象那么链接缓冲池将简单地更 新内部的缓冲链接队列并将该 PooledConnection 对象返回。若是在缓冲池内没 有找到现成的 PooledConnection 对象,那么 ConnectionPoolDataSource 接口将会被 用来产生一个新的 PooledConnection 对象并将它返回以便应用程序使用编程

6。 中间层服务器调用 PooledConnection 对象的 getConnection() 方法以便返还一个 java.sql.Connection 对象给当前的 Java 应用程序windows

七、 当中间层服务器调用 PooledConnection 对象的 getConnection() 方法时, JDBC 数据 库驱动程序将会建立一个 Connection 对象而且把它返回中间层服务器api

八、 中间层服务器将 Connection 对象返回给应用程序 Java 应用程序,能够认为这个 Connection 对象是一个普通的 JDBC Connection 对象使用它能够和数据库创建。事 实上的链接与数据库引擎产生交互操做 。浏览器

九、 当应用程序不须要使用 Connection 对象时,能够调用 Connection 接口的 close() 方 法。请注意这种状况下 close() 方法并无关闭事实上的数据库链接,仅仅是释 放了被应用程序占用的数据库链接,并将它还给数据库链接缓冲池,数据库链接 缓冲池会自动将这个数据库链接交给请求队列中下一个的应用程序使用。

 

如今,数据库的链接没有用到链接池几乎不多不多,每一个项目组均可能有本身的数据库链接池组件,各容器提供商也提供了各自的数据库链接池,下面介绍一下tomcat的数据库链接管理。
tomcat6 数据源配置(server.xml方式和context.xml方式)

在server.xml下配置你必需重启服务器才能生效,而context.xml配置保存后tomcat会自动加载无需重启 
在JNDI配配置数据源中需注意:项目下须要引入数据库驱动包,而且TOMCAT下也须要引入,否则会报错的
1.context.xml方式
Tomcat-6.0.26\conf\context.xml文件当添加如下的配置信息:
<Resource name="jdbc/mysql" auth="Container" type="javax.sql.DataSource"
               maxActive="100" maxIdle="30" maxWait="60" wait_timeout="18800" timeBetweenEvictionRunsMillis="300000" minEvictableIdleTimeMillis="600000"
               username="root" password="jdzxdb" driverClassName="com.mysql.jdbc.Driver"
               url="jdbc:mysql://localhost:3306/sxtele?comautoReconnect=true&amp;failOverReadOnly=false"  removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"/>
<Resource name="jdbc/db2" auth="Container" type="javax.sql.DataSource"
               maxActive="100" maxIdle="30" maxWait="60" wait_timeout="18800" timeBetweenEvictionRunsMillis="300000" minEvictableIdleTimeMillis="600000"
               username="lcgluser" password="lcgluser" driverClassName="com.ibm.db2.jcc.DB2Driver"
               url="jdbc:db2://133.64.46.65:50000/STEDWDB"  removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"/>
 其中:
name 表示指定的jndi名称
auth 表示认证方式,通常为Container
type 表示数据源床型,使用标准的javax.sql.DataSource
maxActive 表示链接池当中最大的数据库链接
maxIdle 表示最大的空闲链接数
maxWait 当池的数据库链接已经被占用的时候,最大等待时间
logAbandoned 表示被丢弃的数据库链接是否作记录,以便跟踪
username 表示数据库用户名
password 表示数据库用户的密码
driverClassName 表示JDBC DRIVER
url 表示数据库URL地址

 

注意,这里你配置的name值要和程序中使用的是同样的,好比按照这个例子,程序就应该是这样的

Java代码 
String gENV = "java:comp/env/";   
Context ctx = new InitialContext();     
      DataSource ds = (DataSource)ctx .lookup(gENV+"jdbc/mysql");   
      Connection conn = ds.getConnection(); 

String gENV = "java:comp/env/";
Context ctx = new InitialContext();
      DataSource ds = (DataSource)ctx.lookup(gENV+"jdbc/db2");
      Connection conn = ds.getConnection();

 

关于获取数据源的语法,大致有(javax.sql.DataSource) ctx.lookup ("java:comp/env/XXX")和(javax.sql.DataSource) ctx.lookup("XXX")两种写法,好多人觉得这两种写法是相同的,觉得都是经过JNDI来获取数据源。其实java:comp/env和 JNDI是不一样的,java:comp/env 是环境命名上下文(environment naming context(ENC)),是在EJB规范1.1之后引入的,引入这个是为了解决原来JNDI查找所引发的冲突问题,也是为了提升EJB或者J2EE应用的移植性。ENC是一个引用,引用是用于定位企业应用程序的外部资源的逻辑名。引用是在应用程序部署描述符文件中定义的。在部署时,引用被绑定到目标可操做环境中资源的物理位置(JNDI名)。使用ENC是把对其它资源的JNDI查找的硬编码解脱出来,经过配置这个引用能够在不修改代码的状况下,将引用指向不一样的EJB(JNDI)。 在J2EE中的引用经常使用的有: 
---------JDBC 数据源引用在java:comp/env/jdbc 子上下文中声明 
---------JMS 链接工厂在java:comp/env/jms 子上下文中声明 
---------JavaMail 链接工厂在java:comp/env/mail 子上下文中声明 
---------URL 链接工厂在 java:comp/env/url子上下文中声明 


 

2.在server.xml文件中配置数据源
Tomcat-6.0.26\conf\context.xml中的配置:在<Host></Host>中添加以下配置
  <Context path="/sljx" reloadable="true"  docBase="sljx"   crossContext="true" 
debug="0">
               <Resource 
                   name="jdbc/oracle"
                   type="javax.sql.DataSource"
                   auth="Container"
                   username="sljx"
                   password="sljx"
                   driverClassName="oracle.jdbc.driver.OracleDriver"
                   maxIdle="3000"
                   maxWait="5000"
                   url="jdbc:oracle:thin:@localhost:1521:jadetrus"
                   autoReconnect="true"
                   maxActive="1000"/>
      </Context>
  具体属性解释和JAVA获取数据源跟上面同样

 

3.在hibernate中配置JNDI也很简单在hibernate.cfg.xml中
<property name="connection.datasource">java:/comp/env/jdbc/mysql</property>   
    <property name="dialect">   
        org.hibernate.dialect.MySQLDialect   
    </property> 


经常使用获取示例
package com.cn.util.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class DBCache {
 private InitialContext ic;

 private Map<String,DataSource> cache;

 private static DBCache instance = new DBCache();

 private DBCache() {
  try {
   ic = new InitialContext();
  } catch (NamingException e) {
   e.printStackTrace();
  }
  cache = Collections.synchronizedMap(new HashMap<String,DataSource>());
 }

 public static DBCache getInstance() {
  return instance;
 }

 
 public Connection getDBConn(String dataSourceName) throws SQLException,
   NamingException {
  Connection conn = null;
        conn=getDataSource(dataSourceName).getConnection();
       return conn;
 }

 public DataSource getDataSource(String dataSourceName)
   throws NamingException {

  if (cache.containsKey(dataSourceName)) {
   return cache.get(dataSourceName);
  } else {

   DataSource newDataSource = (DataSource) ic.lookup(dataSourceName);
   cache.put(dataSourceName, newDataSource);
   return newDataSource;
  }

 }
 public void removeDateSouceOffCache(String dataSourceName) {
  try {
   DataSource ds = cache.get(dataSourceName);
   ds.getConnection().close();
   cache.remove(dataSourceName);
  } catch (Exception ex) {
  }
 }
 
 
 public static void freeConn(Connection conn) {
  freeConn(conn, null, null);
 }

 public static void freeConn(Connection connection, Statement statement) {
  freeConn(connection, statement, null);
 }

 
 public static void freeConn(Connection conn, Statement ps, ResultSet rs) {
  try {
   try {
    if (rs != null)
     rs.close();
   } finally {
    try {
     if (ps != null)
      ps.close();
    } finally {
     if (conn != null)
      conn.close();
    }
   }
  } catch (SQLException ex) {
   ex.printStackTrace();
  }
 }
}

若是获取链接调用getDBConn传入下面两值中任意一个便可
 public static final String DATA_SOURCE = "java:comp/env/jdbc/localhostMysql";
 public static final String DATA_SOURCE_DB2 = "java:comp/env/jdbc/db2";
        private static final String DATA_SOURCE="java:comp/env/jdbc/oracle";

 

关于JNDI的更多用法
基于JNDI的应用开发 
    JNDI(The Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API.命名服务将名称和对象联系起来,使得咱们能够用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。

    命名或目录服务使你能够集中存储共有信息,这一点在网络应用中是重要的,由于这使得这样的应用更协调、更容易管理。例如,能够将打印机设置存储在目录服务中,以便被与打印机有关的应用使用。


  JNDI概述 
    咱们你们天天都不知不觉地使用了命名服务。例如,当你在web浏览器输入URL,http://java.sun.com时,DNS(Domain Name System,域名系统)将这个符号URL名转换成通信标识(IP地址)。命名系统中的对象能够是DNS记录中的名称、应用服务器中的EJB组件(Enterprise JavaBeans Component)、LDAP(Lightweight Directory Access Protocol)中的用户Profile.

    目录服务是命名服务的天然扩展。二者之间的关键差异是目录服务中对象能够有属性(例如,用户有email地址),而命名服务中对象没有属性。所以,在目录服务中,你能够根据属性搜索对象。JNDI容许你访问文件系统中的文件,定位远程RMI注册的对象,访问象LDAP这样的目录服务,定位网络上的EJB组件。

    对于象LDAP 客户端、应用launcher、类浏览器、网络管理实用程序,甚至地址薄这样的应用来讲,JNDI是一个很好的选择。

    JNDI架构

    JNDI架构提供了一组标准的独立于命名系统的API,这些API构建在与命名系统有关的驱动之上。这一层有助于将应用与实际数据源分离,所以无论应用访问的是LDAP、RMI、DNS、仍是其余的目录服务。换句话说,JNDI独立于目录服务的具体实现,只要你有目录的服务提供接口(或驱动),你就可使用目录。如图1所示。 图1:JNDI架构

    关于JNDI要注意的重要一点是,它提供了应用编程接口(application programming interface,API)和服务提供者接口(service provider interface,SPI)。这一点的真正含义是,要让你的应用与命名服务或目录服务交互,必须有这个服务的JNDI服务提供者,这正是JNDI SPI发挥做用的地方。服务提供者基本上是一组类,这些类为各类具体的命名和目录服务实现了JNDI接口?很象JDBC驱动为各类具体的数据库系统实现了JDBC接口同样。做为一个应用开发者,你没必要操心JNDI SPI.你只须要确认你要使用的每个命名或目录服务都有服务提供者。

    J2SE和JNDI

    Java 2 SDK 1.3及以上的版本包含了JNDI.对于JDK 1.1和1.2也有一个标准的扩展。Java 2 SDK 1.4.x的最新版本包括了几个加强和下面的命名/目录服务提供者:

    l LDAP(Lightweight Directory Access Protocol)服务提供者 l CORBA COS(Common Object Request Broker Architecture Common Object Services)命名服务提供者 l RMI(Java Remote Method Invocation)注册服务提供者 l DNS(Domain Name System)服务提供者

    更多的服务提供者

    能够在以下网址找到能够下载的服务提供者列表:

    http://java.sun.com/products/jndi/serviceproviders.html 特别有意思的或许是以下网址提供的Windows 注册表JNDI服务提供者:http://cogentlogic.com/cocoon/CogentLogicCorporation/JNDI.xml 这个服务提供者使你能够访问Windows XP/2000/NT/Me/9x的windows注册表。

      也能够在以下网址下载JNDI/LDAP Booster Pack:http://java.sun.com/products/jndi/ 这个Booster Pack包含了对流行的LDAP控制的支持和扩展。它代替了与LDAP 1.2.1服务提供者捆绑在一块儿的booster pack.关于控制和扩展的更多信息能够在以下网站看到: http://java.sun.com/products/jndi/tutorial/ldap/ext/index.html 另外一个有趣的服务提供者是Sun的支持DSML v2.0(Directory Service Markup Language,目录服务标记语言)的服务提供者。DSML的目的是在目录服务和XML之间架起一座桥梁。

    JNDI API

    JNDI API由5个包组成:

    l Javax.naming:包含了访问命名服务的类和接口。例如,它定义了Context接口,这是命名服务执行查询的入口。 l Javax.naming.directory:对命名包的扩充,提供了访问目录服务的类和接口。例如,它为属性增长了新的类,提供了表示目录上下文的DirContext接口,定义了检查和更新目录对象的属性的方法。 l Javax.naming.event:提供了对访问命名和目录服务时的时间通知的支持。例如,定义了NamingEvent类,这个类用来表示命名/目录服务产生的事件,定义了侦听NamingEvents的NamingListener接口。 l Javax.naming.ldap:这个包提供了对LDAP 版本3扩充的操做和控制的支持,通用包javax.naming.directory没有包含这些操做和控制。 l Javax.naming.spi:这个包提供了一个方法,经过javax.naming和有关包动态增长对访问命名和目录服务的支持。这个包是为有兴趣建立服务提供者的开发者提供的。

    JNDI 上下文

    正如在前面提到的,命名服务将名称和对象联系起来。这种联系称之为绑定(binding)。一组这样的绑定称之为上下文(context),上下文提供了解析(即返回对象的查找操做)。其余操做包括:名称的绑定和取消绑定,列出绑定的名称。注意到一个上下文对象的名称能够绑定到有一样的命名约定的另外一个上下文对象。这称之为子上下文。例如,若是UNIX中目录/home是一个上下文,那么相对于这个目录的子目录就是子上下文?例如,/home/guests中guests就是home的子上下文。在JNDI中,上下文用接口javax.naming.Context表示,这个接口是与命名服务交互的关键接口。在Context(或稍后讨论的DirContext)接口中的每个命名方法都有两种重载形式:

    Lookup(String name):接受串名 l
    Lookup(javax.naming.Name):接受结构名,
    例如,CompositeName(跨越了多个命名系统的名称)或CompondName(单个命名系统中的名称);它们都实现了Name接口。Compound name的一个例子是:cn=mydir,cn=Q Mahmoud,ou=People,composite name的一个例子是:cn=mydir,cn=Q Mahmoud,ou=People/myfiles/max.txt(这里,myfiles/max.txt是表示第二部分的文件名)

        Javax.naming.InitialContext是实现了Context接口的类。用这个类做为命名服务的入口。为了建立InitialContext对象,构造器以java.util.Hashtable或者是其子类(例如,Properties)的形式设置一组属性。下面给出了一个例子:

    Hashtable env = new Hashtable(); // select a service provider factory env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContext"); // create the initial context Context contxt = new InitialContext(env);

    INITIAL_CONTEXT_FACTORY指定了JNDI服务提供者中工厂类(factory class)的名称。Factory负责为其服务建立适当的InitialContext对象。在上面的代码片段中,为文件系统服务提供者指定了工厂类。表1给出了所支持的服务提供者的工厂类。要注意的是文件系统服务提供者的工厂类须要从Sun公司单独下载,J2SE 1.4.x没有包含这些类。

    表1:上下文INITIAL_CONTEXT_FACTORY的值       Name Service Provider Factory 
               File System                                                               com.sun.jndi.fscontext.RefFSContextFactory 
               LDAP                                                                       com.sun.jndi.ldap.LdapCtxFactory 
               RMI                                                                         com.sun.jndi.rmi.registry.RegistryContextFactory 
               CORBA                                                                   com.sun.jndi.cosnaming.CNCtxFactory 
               DNS                                                                         com.sun.jndi.dns.DnsContextFactory

    为了用名称从命名服务或目录中取得或解析对象,使用Context的lookup方法:Object obj=contxt.lookup(name)。Lookup方法返回一个对象,这个对象表示的是你想要找的上下文的儿子。
----------------------------------------------------------------------------------------------------------------------------
对jndi整体的理解: jndi(java naming and directory Interface)它提供了一套使用命名和目录服务的接口。用户能够经过它来使用命名和目录服务。就像jdbc同样。jndi包括命名服务和目录服务两部分,其中目录服务包含目录对象directory object,它包含若干属性对象。提供了对属性的不少操做。

命名和目录服务: 命名和目录服务咱们一直在使用,如操做系统的文件系统,它给咱们提供对文件的操做,查询,添加删除等功能。DNS服务将url同ip地址绑定在了一块儿。命名和目录系统的最主要的功能是将name和对象绑定。在它的基础之上还提供更多的功能如lookup,search.并且存储的对象是有必定层次结构的。使用这样的服务咱们能够对对象更加有效的管理和操做。

命名服务将一个名称映射到一个对象。RMI Registry和CORBA Naming Service都是命名服务。
目录服务也存放对象,但目录服务识别这些对象的相关属性。能够用项目属性来搜索目录。

在20世纪90年代早期,轻量级的目录访问协议(LightWeightDiretoryAccessProtocol,LDAP)被做为一种标准的目录协议被开发出来,JNDI可以访问LDAP。
j2se为jndi提供了5个扩展包:
javax.naming;为访问命名服务提供的api
javax.naming.directory;为访问目录服务提供了基本的接口
javax.naming.event;支持命名和目录服务中的事件通知
javax.naming.ldap; 支持ldap的包,
javax.naming.spi;提供了不一样命名和目录服务能够挂接他们的实现的方法。


context: context是一套name-to-object的绑定(bindings),能够理解为层次或目录,它还可已包括下一层subContext。在使用命名和目录服务时得到initial context是对整个名字空间操做的入口。在目录服务中是DirContext.
----------------------------------------------------------------------------------------------------------------------------

JNDI(Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各类命名和目录服务的通用、统一的接口,相似JDBC都是构建在抽象层上。

JNDI可访问的现有的目录及服务有:
DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol 轻型目录访问协议)、 CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v二、NIS。

JNDI优势:
包含了大量的命名和目录服务,使用通用接口来访问不一样种类的服务;能够同时链接到多个命名或目录服务上;创建起逻辑关联,容许把名称同Java对象或资源关联起来,而没必要指导对象或资源的物理ID。

JNDI程序包:
javax.naming:命名操做;
javax.naming.directory:目录操做;
javax.naming.event:在命名目录服务器中请求事件通知;
javax.naming.ldap:提供LDAP支持;
javax.naming.spi:容许动态插入不一样实现。

利用JNDI的命名与服务功能来知足企业级APIs对命名与服务的访问,诸如EJBs、JMS、JDBC 2.0以及IIOP上的RMI经过JNDI来使用CORBA的命名服务。

JNDI与JDBC:
JNDI提供了一种统一的方式,能够用在网络上查找和访问服务。经过指定一个资源名称,该名称对应于数据库或命名服务中的一个纪录,同时返回数据库链接创建所必须的信息。
代码示例:
try{
Context cntxt = new InitialContext();
DataSource ds = (DataSource) cntxt.lookup("jdbc/dpt");
}
catch(NamingException ne){
...
}

JNDI与JMS:
消息通讯是软件组件或应用程序用来通讯的一种方法。JMS就是一种容许应用程序建立、发送、接收、和读取消息的JAVA技术。
代码示例:
try{
Properties env = new Properties();
InitialContext inictxt = new InitialContext(env);
TopicConnectionFactory connFactory = (TopicConnectionFactory) inictxt.lookup("TTopicConnectionFactory");
...
}
catch(NamingException ne){
...
}

访问特定目录:举个例子,人是个对象,他有好几个属性,诸如这我的的姓名、电话号码、电子邮件地址、邮政编码等属性。经过getAttributes()方法
Attribute attr = directory.getAttributes(personName).get("email");
String email = (String)attr.get();
经过使用JNDI让客户使用对象的名称或属性来查找对象:
foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls);

经过使用JNDI来查找诸如打印机、数据库这样的对象,查找打印机的例子:
Printer printer = (Printer)namespace.lookup(printerName);
printer.print(document);

浏览命名空间:
NamingEnumeration list = namespace.list("o=Widget, c=US");
while (list.hasMore()) {
NameClassPair entry = (NameClassPair)list.next();
display(entry.getName(), entry.getClassName());
}

参考资料:
http://java.sun.com/products/jndi/examples.html
http://java.sun.com/products/jndi/serviceproviders.html

经常使用的JNDI操做:
void bind(String sName,Object object);――绑定:把名称同对象关联的过程
void rebind(String sName,Object object);――从新绑定:用来把对象同一个已经存在的名称从新绑定
void unbind(String sName);――释放:用来把对象从目录中释放出来
void lookup(String sName,Object object);――查找:返回目录总的一个对象
void rename(String sOldName,String sNewName);――重命名:用来修改对象名称绑定的名称
NamingEnumeration listBinding(String sName);――清单:返回绑定在特定上下文中对象的清单列表
NamingEnumeration list(String sName);
代码示例:从新获得了名称、类名和绑定对象。
NamingEnumeration namEnumList = ctxt.listBinding("cntxtName");
...
while ( namEnumList.hasMore() )  {
Binding bnd = (Binding) namEnumList.next();
String sObjName = bnd.getName();
String sClassName = bnd.getClassName();
SomeObject objLocal = (SomeObject) bnd.getObject();
}

----------------------------------------------------------------------------------------------------------------------------

了解名字服务和目录服务的相关概念,有助于更好的使用JNDI。 Naming service       名字服务定义了如何将名字与对象关联,并经过名字如何找到对象的方法。典型的例子如:DNS将域名与IP关联,文件系统将文件名与文件相关联。在名字服务中,主要的概念:

-          名字(Names),在名字系统中实际对象的代号,如文件名,域名等,它会被用来查找关联的对象。不一样的系统中会有不一样的命名规范,如文件系统采用“\”来表示层级,而DNS则使用“.”。

-          绑定(Bindings),名字和实际对象的关联。

-          引用和地址(References and Addresses),当对象不能直接被存储在名字系统时,就必须使用引用,经过引用找到实际的对象。在系统中,保存的引用的内容被称为地址。引用还有另外一个用处:在名字系统中,缺乏象关系数据库中外键的概念。经过使用引用,能够做为外键的一个取代办法。

-          上下文(Context),它是一个名字-对象集合,提供了与名字系统交互的主要操做,如查找、绑定、去绑定。子上下文(subcontext)与它的关系相似文件系统中目录和子目录的关系,子上下文被包含在一个上下文中,经过父上下文中的一个名字与子上下文关联。

-          名字系统和名字空间(Naming Systems and Namespaces),名字系统是相同类型的上下文的集合,它提供名字服务;名字空间,是名字系统中的名字集合,如文件系统的文件名和目录。

Directory service       目录服务是名字服务的扩展,它除了关联名字和对象,还容许对象包含属性。目录系统一般以层次结构组织数据。在目录服务中的主要概念:

-          属性(Attributes),它属于目录对象,它是(名字,值)对,属性能够有多个值。

-          目录和目录服务(Directories and Directory Services),目录是目录对象的集合;目录服务则提供与目录相关的服务,建立、删除和修改存放在目录中的对象的属性。

-          查找和查找过滤器(Searches and Search Filters),获取目录对象的操做就是查找;过滤器是相似查找条件的对象。

基本使用 
 

²        注册JNDI提供者

在使用JNDI以前,须要先获取JNDI的提供者,并在系统注册它。与JNDI相关的系统属性在javax.naming.Context中定义,经常使用的属性:

-          java.naming.factory.initial,服务提供者用来建立InitialContext的类名。

-          java.naming.provider.url,用来配置InitialContext的初始url

-          java.naming.factory.object,用来建立name-to-object映射的类,用于NameClassPair和References。

-          java.naming.factory.state,用来建立jndi state的类

对于目录服务,因为通常须要安全设置,还一般使用:

-          java.naming.security.authentication,安全类型,三个值:none,simple或strong。

-          java.naming.security.principal,认证信息。

-          java.naming.security.credentials,证书信息。

-          java.naming.security.protocol,安全协议名。

使用System.setProperty注册,若是程序不显示说明,那么java会在classpath内查找jdni.properties文件来完成注册。jdni.properties例子:

java.naming.factory.initial=com.codeline.db.MockInitialContextFactory

 

 【链接服务】

注册以后,就能够实施服务链接了。对于名字服务由InitialContext开始,目录服务则使用InitialDirContext。它们分别实现了Context和DirContext,这两个接口分别对应名字服务和目录服务的接口,也是JNDI中最重要的两个接口。

链接名字服务:                 
System.setProperty(Context.INITIAL_CONTEXT_FACTORY," 
com.sun.jndi.fscontext.FSContextFactory"); 
InitialContext ctx = new InitialContext();

链接目录服务:
  Hashtable env = new Hashtable(); 
   env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
   env.put(Context.PROVIDER_URL, "ldap://myserver.com/");
   env.put(Context.SECURITY_AUTHENTICATION, "simple"); 
    //登陆ldap server须要的用户名 
   env.put(Context.SECURITY_PRINCIPAL, "ldapuser"); 
  //登陆ldap server须要的密码 
   env.put(Context.SECURITY_CREDENTIALS, "mypassword"); 
InitialDirContext ctx = new InitialDirContext(env);

 

多服务提供者:若是应用包含多个服务提供者,在链接时略有不一样。以名字服务为例
Hashtable env = new Hashtable(); 
  env.put(Context.INITIAL_CONTEXT_FACTORY, 
"com.sun.jndi.rmi.registry.RegistryContextFactory"); 
  env.put(Context.PROVIDER_URL, "rmi://myserver.com:1099"); 
  //使用不一样的构造函数 
InitialContext ctx = new InitialContext(env); 
 

【查找对象】

不论名字服务仍是目录服务,都是使用lookup来查找对象的。除了可使用String做为参数以外,lookup还可以使用Name接口做为参数。
Greeter greeter = (Greeter)ctx.lookup("SayHello"); 
若是想要得到上下文中全部的对象名字,就使用lis返回NameClassPair列表。NameClassPair包含对象名字和对象类名。若是想要得到实际的对象实例列表,就使用listBindings,它返回Binding列表。Binding是NameClassPair的子类,它包含对象的实例。 
-  list

NamingEnumeration list = ctx.list("awt"); 
while (list.hasMore()) { 
    NameClassPair nc = (NameClassPair)list.next(); 
    System.out.println(nc); 
}

-  listBindings

NamingEnumeration bindings = ctx.listBindings("awt"); 
while (bindings.hasMore()) { 
    Binding bd = (Binding)bindings.next(); 
    System.out.println(bd.getName() + ": " + bd.getObject()); 

 

【对象绑定】

- 使用bind添加绑定
Fruit fruit = new Fruit("orange");
ctx.bind("favorite", fruit);

- 使用rebind修改绑定
Fruit fruit = new Fruit("lemon");
ctx.rebind("favorite", fruit);

- 使用unbind去除绑定。
ctx.unbind("favorite"); 
 

【对象更名】

使用rename能够给一个在上下文中的对象更名
ctx.rename("report.txt", "old_report.txt");

- 获取属性

属性相关的接口是Attribute和Attributes,它们都在javax.naming.directory包内。经过DirContext的getAttributes方法就能够得到对象的属性集合,而后使用Attributes的get方法得到对应的属性,最后经过Attribute的get方法就能够得到属性值。


String dn = "uid=me, dc=mycompany, dc=com, ou=customer, o=ExampleApp"; 
Context user = (Context)ctx.lookup(dn); 
//得到全部属性 
Attributes attrs = user.getAttributes(""); 
Attribute test= attrs .get("test"); 
Object testValue= test.get(); 
上例中得到的是user的全部属性,在实际使用过程当中,考虑网络带宽的影响,能够设置获取要获取的属性列表:


String reqd_attrs = new String[] { "surname", "initials","title", "rfc822mailalias"}; 
Attributes attrs = user.getAttributes("", reqd_attrs); 
 

【查找和过滤】

使用search方法完成。
    public DirContext[] findUser(String initials,String surname,String country,String phone) { 
       //构造条件 
         BasicAttributes search_attrs = new BasicAttributes(); 
        search_attrs.put("initials", initials); 
        search_attrs.put("sn", surname); 
        search_attrs.put("c", country); 
       if(phone != null) 
          search_attrs.put("phonenumber", phone); 
        NamingEnumeration results = initial_ctx.search("ou=Customer,o=ExampleApp", search_attrs); 
        LinkedList found = new LinkedList(); 
        while(results.hasMore()) { 
            SearchResults sr = (SearchResults)results.next(); 
            String name = sr.getName(); 
            Object ctx = sr.getObject(); 
           if((ctx == null) || !(ctx instanceof DirContext)) 
               found.add(initial_ctx.lookup(name)); 
           else 
               found.add(ctx); 
       } 
       DirContext[] ret_val = new DirContext[found.size()]; 
        found.toArray(ret_val); 
       return ret_val; 
  } 
 

DirContext接口主要过滤方式: 
1.使用过滤字符串 
String reqd_attrs = new String[] { "cn", "uid","rfc822mailalias" }; 
NamingEnumeration results = initial_ctx.search("ou=Customer, o=ExampleApp",search_attrs,reqd_attrs); 
 
2.使用SearchControls,得到更多的控制 
SearchControls ctrls = new SearchControls(); 
ctrls.setCountLimit(20); 
ctrls.setTimeLimit(5000); 
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE); 
NamingEnumeration results = initial_ctx.search("cat=books,ou=Products, 
o=ExampleApp","title=*Java*",ctrls); 
 【修改属性 】
使用DirContext和InitialDirContext的modifyAttributes方法完成。所谓的修改过程,实际就是先构造要修改的属性列表,而后使用上述方法提交。对于属性包含多个值时,须要把属性的不修改的值也要包含,不然服务器会认为那些值再也不须要而删除它们。
 public void updateAddress(String dn,String address, String country, String phone) { 
        BasicAttributes mod_attrs = new BasicAttributes(); 
        if(address != null) 
            mod_attrs.put("address", address); 
         if(country != null) 
             mod_attrs.put("c", country); 
        if(phone != null) 
             mod_attrs.put("phonenumber", phone); 
         if(mod_attrs.size() != 0) 
            initial_ctx.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, mod_attrs); 

使用ModificationItem,也可一次进行多个不一样的修改操做: 
ModificationItem[] mod_items = new ModificationItems[2]; 
Attribute email = new BasicAttribute("rfc822mailalias", new_email); 
ModificationItem email_mod = new ModificationItem(DirContext.ADD_ATTRIBUTE, email); 
Attribute addr = new BasicAttribute("address", address); 
ModificationItem addr_mod = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, addr); 
mod_items[0] = email_mod; 
mod_items[1] = addr_mod; 
initial_ctx.modifyAttributes(dn, mod_items);

 【建立上下文】
使用createSubcontext方法完成。
 BasicAttributes attrs = new BasicAttributes(); 
attrs.put("initials", initials); 
 attrs.put("sn", surname); 
 attrs.put("rfc822mailalias", email); 
 if(address != null) 
     attrs.put("address", address); 
 if(country != null) 
     attrs.put("c", country); 
 if(phone != null) 
    attrs.put("phonenumber", phone); 
initial_ctx.createSubcontext(dn, attrs); 
 

【删除上下文】

使用destroySubcontext方法完成。initial_ctx.destroySubcontext(dn); 

相关文章
相关标签/搜索