JNDI规范是一个非常老的规范了,我想任何一个在java领域工作的人都应该接触过JNDI,本文算是给Tomcat的JNDI系列的源码分析来一个开头,让大家热热身,后续的一周时间,我们会深入分析Tomcat的是如何实现JNDI的。
1.包分析
严格意义上来讲,其实JNDI规范并没有对应的JCP社区的JSR规范,因为很早时间随着JDK就已经发布了,导致没有对应的PDF可以下载,但是JAVA EE 6的规范中有对应的JNDI规范如何在JAVA EE规范中进行实施,也就是如何绑定和查找JAVA EE对象,这块我们下一节再详细阐述。
在JDK中的几个包如下:

其中,我们关心的Context,IntialContext等接口都在javax.naming包中,这个包是JNDI的API包,

Context:上下文接口
InitialContext:初始化上下文,可以理解为JNDI的入口,非常的重要
而javax.naming.spi是JNDI的实现层,可以有下图进行理解:

处于核心的地位是NamingManager,也就是名称管理器,我们大多数的情况作为客户端,一般是不需要和NamingManager打交道的;
对于JNDI,其最终实现可以理解为一个存储设施,例如树就是一个比较经典的做法,你可以在内存中为其构造一个树,如Tomcat,OpenEJB中,大多数应用服务器都提供了JNDI的查找,我们可以看一下国产应用服务器TongWeb的JNDI树:

不单单能清晰的展现出树的形态,并且当你点击左侧的节点,TongWeb应用服务器会非常人性化的告诉你,你当前该节点挂载的是什么类型的,你需要怎么进行查找,实在是非常人性化之举,而像一些开源的,如Tomcat,Openejb这些,你只能通过JNDI接口去获取这些信息,实在是不直观,而这也就是商业服务器和开源服务器的差距的地方了。
当然,从上述的包的分析来看,除了内存中的类似于树的结构,你也可以将这些信息存入目录服务中,因为目录服务如LDAP,天生就是树状的,和JNDI的需求是类似的,因此javax.naming.directory,javax.naming.ldap这两个包就是JNDI如何支持目录服务的,尤其是javax.naming.ldap这个包,基本上实现了LDAP V3的协议,你完全可以通过LDAP的API去实现JNDI。
对于SPI来讲,JDK中的Naming默认提供了目录服务,从上图我们也可以看到,其实可以有n多种选择,这种选择主要就由javax.spi包来实现了:

其中上图中的三个类是最为关键。
InitialiContextFactory:主要是初始化上下文InitialContext的生成工厂,它能生成InitialContext,所以至关重要,因为我们在客户端需要在环境变量率先指定这个工厂,如在Jboss中,我们在代码中需要将工厂设置到env中:

在Tomcat中,InitialiContextFactory就是javaURLContextFactory,你需要设置这个:

同时,在Tomcat比较有意思的是,这个javaURLContextFactory同时也继承了ObjectFactory:

那什么是ObjectFactory呢?
就是当JNDI创建一些对象绑定的时候,不同类型的节点会有不同的instance实例的创建方式,从下面的类的继承关系可以看:

数据源,ejb,rmi绑定,不同的JNDI对应的实体节点其对象创建也不同,因此需要实现不同的Objectfactory;
而javaURLContextFactory恰恰是创建默认的Context节点类型的,所以从代码上你可以看到其返回的还是SelectorContext,依然是Context接口类型;
javax.naming.directory主要是一些目录操作:

怎么理解这个目录包呢,就是
目录(Directory)可看作是对命名(Naming)的一个扩充,一个目录对象不仅像命名一样,而且还提供的对属性(Attributes)的操作.由API文档可知,javax.naming.directory.DirContext类扩展自Context接口,同样,javax.naming.directory.InitialDirContext也扩展自 javax.naming.InitialContext,由此也可看出目录操作完全支持命名操作。
下面给出一个DNS Service Provider例子以演示有关目录的一些操作:
public class TestDNSJndi {
public static void main(String[] args) throws Exception {
Properties env= newProperties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
//此IP一定要为要访问的DNS服务器的IP,可通过网络设置查看
env.put(Context.PROVIDER_URL, "dns://10.17.45.239");
DirContext ctx= newInitialDirContext(env);
System.out.println("a:" + ctx);
DirContext ctx1 =(DirContext) ctx.lookup("www.sina.com");
System.out.println("b:" + ctx1);
printAttributes("c:", ctx1.getAttributes(""));
//从ctx.getAttributes("www.sina.com")与ctx1.getAttributes("")结果一样
printAttributes("d:", ctx.getAttributes("www.sina.com"));
Attributes attrs1 = ctx.getAttributes("www.sina.com", newString[] { "a" });
Attributes attrs2 = ctx.getAttributes("www.163.com", newString[] { "a" });
Attributes attrs3 = ctx1.getAttributes("", new String[] { "a" });
Attributes attrs4 = ctx.getAttributes("www.baidu.com", newString[] { "a" });
printAttributes("e:", attrs1);
printAttributes("f:", attrs2);
printAttributes("g:", attrs3);
printAttributes("attrs4:", attrs4);
System.out.println("nameParse:"+ctx1.getNameInNamespace());
//list,此方法会导致程序lock
//listEnumation("list:",ctx.list(""));
//----------------------search
Attributes matchAttrs =new BasicAttributes(true);
matchAttrs.put(new BasicAttribute("a", "61.172.201.13"));
NamingEnumeration answer = ctx1.search("www.sina.com", matchAttrs);
printNamingEnumeration("search :", answer);
}
下一篇我们会继续分析这些JNDI的环境变量,请期待!




