JNDI详解

本文上半部分来自《经典javaEE企业应用实战(李刚)》

JNDI的全称是Java Naming Directory Interface,即java命名和目录接口,它允许java程序通过一个名字来访问真正的java对象。JNDI提供了API来访问命名目录服务,它独立于命名目录服务器。

命名服务就是将对象与名字关联,通过名字即可访问对象。JNDI是一种规范,它的实现可以管理一组对象,包括java对象,EJB,RMI中访问的远程对象等。

目录服务是命名服务的扩展,它不仅保存对象与名字的关联,而且管理对象的属性信息。

命名目录服务除了JNDI之外,还有LDAP(light directory access Protocol),Microsoft active directory等。

JNDI接口在javax.naming包中,由两部分API组成:JNDI的API和JNDI的SPI,前者让编程人员可以一致地使用各种命名目录服务,而后者则是让各种命名目录服务可以透明地加入到JNDI结构中,负责管理二者之间转换的是NamingManager。

关于JNDI的基本概念:

Binding:一个名字到一个对象的绑定,类似于键值对吧

Context:binding的集合,其中的名字必须是唯一的,它可以有SubContext,类似文件目录结构吧

JNDI的使用需要三方面API的支持:

JNDI的API,JDK中已经定义了一系列的接口,在javax.naming包及其子包中。

JNDI的SPI,底层的命名目录服务是通过它完成的,在JDK6中,有如下的SPI:COS,LDAP,DNS,RMI,CORBA。唯一可以在java程序中直接启动的就是基于RMI的JNDI服务了,其他的都需要额外的包。

命名目录服务器,若以文件系统作为命名目录服务器,则不需要另外安装。


OK,说了这么多理论知识,接下来看一个通过JNDI来使用文件系统命名服务的例子,了解JNDI是如何使用的。

首先,下载两个文件系统命名服务需要的SPI包:点击打开链接,并添加到classpath下。

编写测试文件:

import javax.naming.*;
import java.util.*;
public class NamingServiceTest{	
	public static void main(String[] args) throws NamingException{
		Hashtable env = new Hashtable();
		env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContextFactory");
		env.put(Context.PROVIDER_URL,"file:/e:/java");		
		/*
		 * INITIAL_CONTEXT_FACTORY:实际实例化的工厂类,由该工厂创建一个Context对象
		 * PROVIDER_URL:命名服务的提供者
		 * InitialContext对象的构造需要以上两个属性值,如果没指定的话,使用System.getProperty()来获取,
		 * 如果获取不到,就抛出异常
		 * */
		Context ctx = new InitialContext(env);
		
		Object file = ctx.lookup("ABCD.java");
		System.out.println("abcd.java文件被绑定到了:"+file);
		
		Object dir = ctx.lookup("books");
		System.out.println("books文件夹被绑定到了:"+dir);
		
		ctx.close();
	}
}

输出:

abcd.java文件被绑定到了:e:\java\ABCD.java
books文件夹被绑定到了:com.sun.jndi.fscontext.RefFSContext@665753

下面再用一个例子来说明Context接口的其他方法的使用:

import javax.naming.*;
import java.util.*;
public class NamingServiceTest2{	
	public static void main(String[] args) throws NamingException{
		Hashtable env = new Hashtable();
		env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContextFactory");
		env.put(Context.PROVIDER_URL,"file:/e:/java");	
		Context ctx = new InitialContext(env);
		
		//列出java目录下所有的文件和文件夹
		NamingEnumeration<Binding> bindings = ctx.listBindings("");
		while(bindings.hasMore()){
			Binding binding = bindings.next();
			System.out.println(binding.getName()+" 绑定到:"+binding.getObject());
		}
		
		//对文件进行重命名
		ctx.rename("DCBA.java", "ABCD.java");
		
		//创建了两个文件夹
		ctx.createSubcontext("newdir1");
		ctx.createSubcontext("newdir2");		
		
		//删除文件夹
		ctx.destroySubcontext("newdir2");		
		ctx.close();
	}
}

这样的例子似乎离我们很远,我们不会这样来使用JNDI,那么接下来看看在J2EE中如何使用JNDI。

通常,我们在xml文件中配置一个数据源,我们在应用的其他位置就可以通过名字来访问这个数据源了。其实,是Tomcat幕后将这个数据源对象放到了JNDI结构中,我们才能那么方便地获取数据源对象,然后获得到数据库的连接。

在JSP页面中调用System.getProperties(),从输出可以看到如下这两行:

java.naming.factory.initial:org.apache.naming.java.javaURLContextFactory

java.naming.factory.url.pkgs:org.apache.naming

可见,我们之所以可以在Tomcat的程序中直接使用new InitialContext()而不用传递参数,是Tomcat为我们配置好了属性。在Tomcat的Catalina的jar包中,可以找到naming包,里面应该就是对JNDI的支持了。

这里给出一个详细的例子吧。

xml文件:

<Context path="" docBase="" reloadable="true">
  <Resource name="jdbc/DBPool" auth="Container" type="javax.sql.DataSource"    
   	 maxActive="100"    
     <span style="white-space:pre">	</span> maxIdle="30"  
   	 maxWait="10000"  
   	 username="sa"    
   	 password="sa"  
   	 driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"  
   	 url="jdbc:sqlserver://localhost:1433;databasename=dbtest"/>  
 </Context>

DBServlet:

package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

public class DBServlet extends HttpServlet{
	
	@Override
	public void doGet(HttpServletRequest request,HttpServletResponse response) 
			throws IOException,ServletException{	
			 try{
	       Context context = new InitialContext();        		
	       DataSource ds = (DataSource)context.lookup("java:comp/env/jdbc/DBPool");
	       response.getWriter().println(ds);
			 }
			 catch(NamingException e){
				 e.printStackTrace();
			 }
	}
	
	@Override
	public void doPost(HttpServletRequest request,HttpServletResponse response) 
			throws IOException,ServletException{
		doGet(request,response);
	}

}

访问.../db就可以看到不是一个null之,也没抛出异常,说明已经获得了在web.xml中配置的数据源了。至于获得之后,那就是数据库编程了。


JNDI还可以管理其他的对象了,后面再慢慢说吧。

相关文章
相关标签/搜索