本文上半部分来自《经典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
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 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();
}
}
通常,我们在xml文件中配置一个数据源,我们在应用的其他位置就可以通过名字来访问这个数据源了。其实,是Tomcat幕后将这个数据源对象放到了JNDI结构中,我们才能那么方便地获取数据源对象,然后获得到数据库的连接。
在JSP页面中调用System.getProperties(),从输出可以看到如下这两行:
java.naming.factory.initial:org.apache.naming.java.javaURLContextFactory
java.naming.factory.url.pkgs:org.apache.naming
这里给出一个详细的例子吧。
xml文件:
maxIdle="30"
maxWait="10000"
username="sa"
password="sa"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
url="jdbc:sqlserver://localhost:1433;databasename=dbtest"/>
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);
}
}
JNDI还可以管理其他的对象了,后面再慢慢说吧。