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) 服务提供程序。
(图片转载自: http://lavasoft.blog.51cto.com/62575/191633)
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");
而文件系统的JNDI应用程序实例更能贴切的说明:JNDI是
Java应用程序中访问名称和目录服务的一组API。
import java.io.File;
import java.util.Hashtable;
import java.util.Properties;
import javax.naming.Context;
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");
// 欲获得的文件
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查找数据源也是同样的原理,只是实现的细节不同而已。