1.在源码解析之前,了解下JNDI。
JNDI(Java Naming and Directory Interface,Java 命名和目录服务接口)是用于从Java应用程序中访问名称和目录服务的一组API,简化了企业应用组件(也称构件)之间的查找调用。
简单的说是:可以使用一种简单的方式去查找某种资源。
这就像一个公用电话簿,企业应用组件在命名环境注册登记,并且通过命名环境查找所需其他组件。
2.JNDI架构
JNDI架构提供了一个标准的、与命名系统无关的API,这个API构建在特定于命名系统的驱动程序之上。这一层帮助把应用程序和实际的数据源隔离开来,因此无论应用程序是访问LDAP、RMI、DNS还是其他的目录服务,这都没有关系。换句话说,JNDI与任何特定的目录服务实现无关,您可以使用任何目录,只要您拥有相应的服务提供程序接口(或驱动程序)即可,如图下图所示。

注意,关于JNDI有一点很重要,即它同时提供应用程序编程接口(Application Programming Interface ,API)和服务提供程序接口(Service Provider Interface ,SPI)。这样做的实际意义在于,对于您的与命名或目录服务交互的应用程序来说,必须存在用于该服务的一个JNDI服务提供程序,这便是JNDI SPI发挥作用的舞台。一个服务提供程序基本上就是一组类,这些类针对特定的命名和目录服务实现了各种JNDI接口——这与JDBC驱动程序针对特定的数据系统实现各种JDBC接口极为相似。作为一名应用程序开发人员,您不需要担心JNDI SPI.。您只需确保,您为每个想使用的命名或目录服务提供了一个服务提供程序。
J2SE和JNDI
JNDI被包含在Java 2 SDK 1.3 及其更新版本中。它还可以用作JDK 1.1和1.2的一个标准扩展。 Java 2 SDK 1.4.x的最新版本进行了改进,将以下命名/目录服务提供程序包括进来:
轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP) 服务提供程序。
公共对象请求代理架构(Common Object Request Broker Architecture ,CORBA)公共对象服务(Common Object Services ,COS)命名服务提供程序。
Java远程方法调用( Remote Method Invocation ,RMI)注册表服务提供程序。
域名系统( Domain Name System ,DNS) 服务提供程序。
轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP) 服务提供程序。
公共对象请求代理架构(Common Object Request Broker Architecture ,CORBA)公共对象服务(Common Object Services ,COS)命名服务提供程序。
Java远程方法调用( Remote Method Invocation ,RMI)注册表服务提供程序。
域名系统( Domain Name System ,DNS) 服务提供程序。
3.应用程序
一般在学习J2EE阶段最初接触到的是在tomcat容器的Server.xml中配置DataSource。
如:<Resource name="jdbc/MyDB" auth="Container" type="javax.sql.DataSource" ..../>
一个JSP/Servlet应用程序。通过JNDI接口使用DataSource服务,如:
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/MyDB");
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/MyDB");
而文件系统的JNDI应用程序实例更能贴切的说明:JNDI是
Java应用程序中访问名称和目录服务的一组API。
import java.io.File;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Hashtable;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class TestFileSystemJNDI {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws NamingException {
// 在此之前要引入 providerutil.jar 和 fscontext.jar Jar包,可以在 http://java.sun.com/products/jndi/downloads/index.html下载fscontext.zip就行
Hashtable environment= new Hashtable();
// 加入初始化上下文(InitialContext)实现的工厂类(com.sun.jndi.fscontext.RefFSContextFactory),负责创建InitialContext对象
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
@SuppressWarnings("unchecked")
public static void main(String[] args) throws NamingException {
// 在此之前要引入 providerutil.jar 和 fscontext.jar Jar包,可以在 http://java.sun.com/products/jndi/downloads/index.html下载fscontext.zip就行
Hashtable environment= new Hashtable();
// 加入初始化上下文(InitialContext)实现的工厂类(com.sun.jndi.fscontext.RefFSContextFactory),负责创建InitialContext对象
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
// 欲获得的文件
String filename = "d://jndi.txt";
// 通过传入Properties构建InitialContext对象
InitialContext context = new InitialContext(environment);
File file = (File)context.lookup(filename);
System.out.println("上下文通过文件名 : " + filename + " 查找到的文件对象" + file);
// 这样我们就可以使用‘file’对象可以对‘d://jndi.txt’进行读取,写入等操作。当然传入的文件名为目录也可以的。
}
}
上述应用程序解析说明:
Context 是一个接口,Sun公司提供的规范。
一。new InitialContext(environment);即创建
InitialContext 对象时:
1.会调用javax.naming.InitialContext类里的public InitialContext(Hashtable<?,?> environment)方法。
2.接着调用InitialContext调用init(environment)方法
3.然后init(environment)通过资源管理器获得初始环境,HashTable myProps = ResourceManager.getInitialEnvironment(environment);
4.通过myProps注入命名管理器getInitialContext方法中,初始化默认的上下文对象Context defaultInitCtx = NamingManager.getInitialContext(myProps);
5.在这里myProps就是env,获得要实现Context接口的工厂类(负责创建Context对象),从注入的myProps取出工厂类名:String className = env != null ?(String)env.get(Context.INITIAL_CONTEXT_FACTORY) : null;然后通过工厂的类名加载并创建工厂实例对象 InitialContextFactory factory = (InitialContextFactory)helper.loadClass(className).newInstance();
6.调用工厂对象创建上下文factory.getInitialContext(env);返回给defaultInitCtx对象。
二。File file = (File)context.lookup(filename);通过上下文查找到指定filename的file对象。
1.调用lookup方法时,会执行getURLOrDefaultInitCtx(name).lookup(name);方法。getURLOrDefaultInitCtx(name)会获得defaultInitCtx对象。
2.defaultInitCtx对象查找filename并生成file对象.defaultInitCtx.getObjectInstance(Object refInfo, Name name, Context nameCtx,Hashtable<?,?> environment)。其中refInfo为实现的File对象,name由filename而来,nameCtx为实现的Context(defaultInitCtx),environment为我们开始传入的HashTable对象。
其实,JNDI查找数据源也是同样的原理,只是实现的细节不同而已。