JNDI

Java术语

英文全称是:Java Naming and Directory Interface

术语解释:一组帮助做多个命名和目录服务接口的API。

JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。集群JNDI实现了高可靠性JNDI[8],通过服务器的集群,保证了JNDI的负载平衡和错误恢复。在全局共享的方式下,集群中的一个应用服务器保证本地JNDI树的独立性,并拥有全局的JNDI树。每个应用服务器在把部署的服务对象绑定到自己本地的JNDI树的同时,还绑定到一个共享的全局JNDI树,实现全局JNDI和自身JNDI的联系。

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&v2、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操作:

常用的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的作用,我们可以从“如果不用JNDI我们怎样做?用了JNDI后我们又将怎样做?”这个问题来探讨。

没有JNDI的做法:
程序员开发时,知道要开发访问MySQL数据库的应用,于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到数据库。
就像以下代码这样:

Connection conn=null;try {  Class.forName("com.mysql.jdbc.Driver",                true, Thread.currentThread().getContextClassLoader());  conn=DriverManager.getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue");  /* 使用conn并进行SQL操作 */  ......  conn.close();} catch(Exception e) {  e.printStackTrace();} finally {  if(conn!=null) {    try {      conn.close();    } catch(SQLException e) {}  }}
这是传统的做法,也是以前非Java程序员(如Delphi、VB等)常见的做法。这种做法一般在小规模的开发过程中不会产生问题,只要程序员熟悉Java语言、了解JDBC技术和MySQL,可以很快开发出相应的应用程序。

没有JNDI的做法存在的问题:
1、数据库服务器名称MyDBServer 、用户名和口令都可能需要改变,由此引发JDBC URL需要修改;
2、数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改;
3、随着实际使用终端的增加,原配置的连接池参数可能需要调整;
4、......

解决办法:
程序员应该不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。

由此,就有了JNDI。

用了JNDI之后的做法:
首先,在在J2EE容器中配置JNDI参数,定义一个数据源,也就是JDBC引用参数,给这个数据源设置一个名称;然后,在程序中,通过数据源名称引用数据源从而访问后台数据库。
具体操作如下(以JBoss为例):
1、配置数据源
在JBoss的 D:\jboss420GA\docs\examples\jca 文件夹下面,有很多不同数据库引用的数据源定义模板。将其中的 mysql-ds.xml 文件Copy到你使用的服务器下,如 D:\jboss420GA\server\default\deploy。
修改 mysql-ds.xml 文件的内容,使之能通过JDBC正确访问你的MySQL数据库,如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
    <jndi-name>MySqlDS</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/lw</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>root</user-name>
    <password>rootpassword</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
    <metadata>
       <type-mapping>mySQL</type-mapping>
    </metadata>
</local-tx-datasource>
</datasources>

这里,定义了一个名为MySqlDS的数据源,其参数包括JDBC的URL,驱动类名,用户名及密码等。

2、在程序中引用数据源:

Connection conn=null;try {  Context ctx=new InitialContext();  Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用数据源  DataSource ds=(Datasource)datasourceRef;  conn=ds.getConnection();  /* 使用conn进行数据库SQL操作 */  ......  c.close();} catch(Exception e) {  e.printStackTrace();} finally {  if(conn!=null) {    try {      conn.close();    } catch(SQLException e) { }  }}直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几,但是现在的程序可以不用关心具体JDBC参数了。
在系统部署后,如果数据库的相关参数变更,只需要重新配置 mysql-ds.xml 修改其中的JDBC参数,只要保证数据源的名称不变,那么程序源代码就无需修改。

由此可见,JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。

JNDI的扩展:
JNDI在满足了数据源配置的要求的基础上,还进一步扩充了作用:所有与系统外部的资源的引用,都可以通过JNDI定义和引用。

所以,在J2EE规范中,J2EE 中的资源并不局限于 JDBC 数据源。引用的类型有很多,其中包括资源引用(已经讨论过)、环境实体和 EJB 引用。特别是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一项关键角色:查找其他应用程序组件。

EJB 的 JNDI 引用非常类似于 JDBC 资源的引用。在服务趋于转换的环境中,这是一种很有效的方法。可以对应用程序架构中所得到的所有组件进行这类配置管理,从 EJB 组件到 JMS 队列和主题,再到简单配置字符串或其他对象,这可以降低随时间的推移服务变更所产生的维护成本,同时还可以简化部署,减少集成工作。 外部资源”。


总结:
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。

在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和预先考虑,这个承诺是完全可以实现的。

----------------------------------TOMCAT中的JNDI------------------------------------------

1.所需要的jar文件:commons-pool.jar, commons-dbcp.jar

2.server.xml的配置:
  <Resource name="jdbc/hellohibernate" scope="Shareable" auth="Container"
   type="javax.sql.DataSource"/> 
   <ResourceParams name="jdbc/hellohibernate"> 
    <parameter> 
    <name>factory</name> 
    <value>org.apache.commons.dbcp.BasicDataSourceFactory</value> 
    </parameter> 
    <parameter>
         <name>removeAbandoned</name>
         <value>true</value>
        </parameter>
    <parameter>
         <name>logAbandoned</name>
         <value>true</value>
        </parameter>
    <!-- DBCP database connection settings --> 
    <parameter> 
    <name>url</name> 
    <value>jdbc:mysql://localhost:3306/test</value> 
    </parameter> 
    <parameter> 
    <name>driverClassName</name><!--<value>com.mysql.jdbc.Driver</value>--> 
    <value>org.gjt.mm.mysql.Driver</value>
    </parameter> 
    <parameter> 
    <name>username</name> 
    <value>root</value> 
    </parameter> 
    <parameter> 
    <name>password</name> 
    <value></value> 
    </parameter> 
  
    <!-- DBCP connection pooling options --> 
    <parameter> 
    <name>maxWait</name> 
    <value>3000</value> 
    </parameter> 
    <parameter> 
    <name>maxIdle</name> 
    <value>100</value> 
    </parameter> 
    <parameter> 
    <name>maxActive</name> 
    <value>10</value> 
    </parameter> 
   </ResourceParams> 
 3.程序中的调用:  
  Context ctx = new InitialContext();
        if (ctx == null)
          throw new Exception("Boom - No Context");
  
        DataSource ds =
          (DataSource) ctx.lookup(
          "java:comp/env/jdbc/hellohibernate");
  
        if (ds != null) {
          Connection conn = ds.getConnection();
  
          if (conn != null) {
            foo = "Got Connection " + conn.toString();
            Statement stmt = conn.createStatement();
            ResultSet rst =
              stmt.executeQuery(
              "select username,id from user");
            if (rst.next()) {
              foo = rst.getString(1);
              bar = rst.getInt(2);
            }
            conn.close();
          }
        }
-------------------------------------------------------------------------------------------------

有关tomcat配置jndi的一些简单介绍:

先说流程:(根据tomcat有关jndi的文档,这个文档应该是针对单个项目配置,下面有比较好的可以配置全局使用的例子)

1.先安装jdbc驱动,这一步很简单,只需将对应数据库驱动放到tomcat对应common/lib目录下就行了;

2.修改对应项目的WEB-INF/web.xml文件,用来声明你的jndi名字以供你的项目使用:

在你的web.xml文件中加入如下代码:

<resource-ref></resource-ref>
  <description></description>
<resource-ref>
  <description>
  Resource reference to a factory for java.sql.Connection
  instances that may be used for talking to a particular
  database that is configured in the server.xml file.
  </description>
  <res-ref-name>
  jdbc/EmployeeDB
  </res-ref-name>
  <res-type>
  javax.sql.DataSource
  </res-type>
  <res-auth>
  Container
  </res-auth>
</resource-ref>
3.对应java代码中获得连接方法:(代码摘要)

Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource)
  envCtx.lookup("jdbc/EmployeeDB");

Connection conn = ds.getConnection();
... use this connection to access the database ...
conn.close();4.配置你的tomcat资源工厂:

在WEB-INF目录下新建文件context.xml,内容如下:

<context></context>
<Context ...>
  ...
  <Resource name="jdbc/EmployeeDB" auth="Container"
  type="javax.sql.DataSource" username="dbusername" password="dbpassword"
  driverClassName="org.hsql.jdbcDriver" url="jdbc:HypersonicSQL:database"
  maxActive="8" maxIdle="4"/>
  ...
</Context>

这里使用HypersonicSQL database JDBC driver作为例子,不同数据库大家可以找对应的字段改一下就行,关于具体字段含义
后面有说明。
大概步骤已经完了,后面大家应该明白怎么做了,关于建立通用的连接池的方法这里不写了,我在后面贴出来英文的,大家慢慢看
以下摘自tomcat关于jndi的官方文档:
所有安装tomcat的可以在tomcat目录下找到对应文章,路径如下:
D:/apache-tomcat-5.5.25/webapps/tomcat-docs/jndi-resources-howto.html
红字标出的是tomcat解压目录,大家改成自己对应目录就可以了
关于context.xml中对应字段解释地址如下:
http://commons.apache.org/dbcp/configuration.html

posted @ 2010-09-20 09:38  【小洲】  阅读(504)  评论(0)    收藏  举报