JNDI即Java命名和目录接口(JavaNaming and Directory Interface),它属于J2EE规范范畴,是J2EE的核心技术之一,提供了一组接口、类和关于命名空间的概念。JDNI是provider-based技术,它暴露一个API和一个服务供应接口(SPI)。它将名称和对象联系起来,使我们可以用名称访问对象。我们可以把JNDI简单地看成是里面封装了一个name到实体对象的映射,通过字符串可以方便得到想要的对象资源,例如JDBC、JMail、JMS、EJB等。这意味着任何基于名字的技术都能通过JNDI而提供服务,现在它支持的技术包含了LDAP、RMI、CORBA、NDS、NIS、DNS、Windows注册表等等。

竟然我们都可以直接各种服务,为什么还要在封装一层JNDI?总的来说,JNDI的出现就是为了分布式开发服务,用一条线把责任任务分成两边,一边的人负责开发这些分布式对象,一边的人只要使用这些分布式对象即可。两边的人不必是同属一个公司,并且两边的人开发通常不是并行的,也不必同属一个项目,就像一个应用服务器的JNDI中注册了一个数据源,不管在哪个项目中,只要有需要就可以通过JNDI使用这个数据源。总结起来就是随着分布式应用的发展,远程访问对象成为常用方法,尽管我们通过socket可以实现远程通信,但要关心每一个远程访问的具体细节并实现,这样局限性还是比较大,效率低下。而伴随着JNDI技术的出现,将大大简化远程调用,方便查找远程或本地的对象。

JNDI包含了很多的服务接口,如图3-1-8-1,JNDIAPI提供了访问不同JNDI服务的一个标准的统一的实现,其具体实现可由不同的Service Provider来完成,具体调用类及通信过程对用户来说是透明的。从架构层面上可以把JNDI分成五个层次,第一层是java编码层,通过它调用第二层的JNDI接口,第三层是JNDI管理器,第四层是JNDI互联网服务提供商接口层,用于规范各个厂商的实现,第五层就是各个厂商具体实现的细节了。

图3-1-8-1

JNDI接口在JDK中包含了5个包,这里有必要稍微介绍一下,

①  javax.naming:这个包下面主要是用于访问命名服务的类和接口。比如说里面定义了Context接口,该接口是执行查找时命名服务的入口点。

②  javax.naming.directory:这个包主要包含提供用访问目录服务的类和接口的扩展命名类和接口。例如,它增加了新的属性类,提供代表一个目录上下文的DirContext 接口,并且定义了用于检查和更新与目录对象相关的属性的方法。

③  javax.naming.event:这个包主要为访问命名和目录服务时提供事件通知以达到监控功能。例如,它定义了一个NamingEvent类,用于表示由命名/目录服务生成的事件,以及一个监视NamingEvents 类的NamingListener 接口。

④  javax.naming.ldap:这个包为LDAP 版本 3 扩展操作和空间提供特定的支持,而普通的javax.naming.directory 包没有提供这些支持。

⑤  javax.naming.spi:这个包提供方法以动态插入对通过javax.naming及其相关包访问命名和目录服务的支持。只有那些对创建服务提供程序有着浓厚兴趣的开发人员才对这个包感兴趣,例如本书主角Tomcat的开发者就必须跟这个包的类打交道。

上面我们了解了JNDI的大体结构,接着主要看看JNDI的运行机制,那么它的结构到底是怎样的,设计又是如何巧妙的呢?可以说jndi的主要工作就是维护操作两个对象:命名上下文和命名对象。他们的关系可以用下图3-1-8-2简单表示,其中圆圈表示命名上下文,星形表示命名上下文所绑定的命名对象。初始上下文为入口,假如你查找的对象的url是“A/C/O3”那么命名上下文将对这个url进行分拆,先找到名字为A的上下文,接着再找到C上下文,最后找到名字为O3的命名对象。类似的其他对象也是如此查找。这便是JNDI树,所有的命名对象和命名上下文都被绑定到树上,一般说来,命名上下文是树上的节点,而命名对象是树上的树叶,不管是命名对象还是命名上下文都有自己的名字。

图3-1-8-2

谈谈命名对象,一般来说在JNDI中存在两种命名对象形态:①直接存在内存中的命名对象;②使用时再根据指定类及属性信息创建命名对象。第一种形态,将实例化好的对象通过Context.bind()绑定到上下文,当需要命名对象时通过Context.lookup()查找,这种情况是直接从内存中查找相应的对象,上下文会在内存中维护所有绑定了的命名对象。这种方式存在几个缺点,首先,内存大小限制了绑定到上下文的对象的数量;其次,一些未持久保存的对象在命名服务重启后不可恢复;最后,有些对象根本就不适合这种方式,例如数据库连接对象;第二种形态,将生成命名对象需要的类位置信息及一些属性信息进行绑定,那么在查找时就可以使用这些信息创建适合Java应用使用的Java对象,这种情况下,在绑定时可能需要额外做一些处理,例如将Java对象转化为对应的类位置信息及一些属性信息,绑定跟查找这两个相反的过程通过ObjectFactory和StateFactory两个工厂的getObjectInstance和getStateToBind方法进行实现。一般来说JNDI提供Reference类作为存储类位置信息及属性信息的标准方式,并鼓励命名对象实现这个类而不是自己另起炉灶,同时Serializable也可作为作为JNDI存储对象类型,表示可序列化的对象,另外Referenceable对象可通过Referenceable.getReference()返回Reference对象进行存储。

JNDI整个框架能正常运转起来,主要是对命名上下文和命名对象的处理进行了巧妙合理设计,我们对其进行解析研究,把大概的骨架摸清楚了后面整体看起来就清晰了。由于涉及到JNDI的源码,这里给出主要的一些类,如图3-1-8-3:

图3-1-8-3

从类图可以看到不管是命名上下文相关的类还是命名对象相关的类都围绕着NamingManager这个类,命名上下文相关的类则提供了上下文实现的一些策略;命名对象相关的类则提供了命名对象存储及创建的一些策略。这些交互总的可以分为两大部分,①通过FactoryBuilder模式、URL模式、环境变量模式三种机制确定初始上下文,相关接口类分别为InitialContextFactoryBuilder接口、xxxURLContextFactory类、InitialContext类;②通过工厂模式定义上下文中绑定和查找对象的转化策略,相关接口类为StateFactory接口、ObjectFactory接口。

围绕着NamingManager的这些类跟接口是JNDI能正常运行的基础,所有的上下文都要实现Context接口,这个接口主要的方法是lookup、bind,分别用于查找对象跟绑定对象。我们熟知的InitialContext即是JNDI的入口,NamingManager包含很多操作上下文方法,有两个方法有必要提一下,getStateToBind及getObjectInstance这两个方法将任意类型的对象转变成适合在命名空间存储的形式及将存储在命名空间中的信息转换成对象,两者是相反的过程,详细的转换策略可以在自定义的xxxFactory工厂类里面自己定义具体的转换策略的实现。另外定义了几个接口,用于约束在整个JNDI机制实现中特定的方法。为了让大家更好理解JNDI的运行机制,下面分步进行说明:

①  实例化InitialContext作为入口。

②  调用InitialContext的lookup或bind等方法。

③  lookup、bind等方法实际是getURLOrDefaultInitialCtx返回的上下文的lookup或bind方法。

④  getURLOrDefaultInitialCtx方法会判断是否用NamingManager的setInitialContextFactorybuilder方法设置了InitialContextFactorybuilder,即判断NamingManager里面的InitialContextFactorybuilder变量是否为空。

⑤  根据④,如果设置了,则会调用InitialContextFactorybuilder的createInitialContextFactory方法返回一个InitialContextFactory,再调用这个工厂类的getInitialContext返回Context,至此得到了上下文。

⑥  根据④,如果没设置,则获取url的scheme,例如“java:/comp/env”中java即为这个url的scheme,接着根据scheme继续判断怎么生产上下文。

⑦  根据⑥,如果scheme不为空时,则根据Context.URL_PKG_PREFIXES变量的值作为工厂的前缀,然后指定上下文工厂类路径,规则为:前缀.scheme.schmeURLContextFactory,例如前缀值为com.sun.jndi,scheme为java,则工厂类的路径为com.sun.jndi.java.javaURLContextFactory,接着调用工厂类的getObjectInstance返回上下文。如果按照上面获取上下文失败则根据Context.INITIAL_CONTEXT_FACTOR变量指定的工厂类生成上下文。

⑧  根据⑥,如果scheme为空时,则根据实例化InitialContext时传入的Context.INITIAL_CONTEXT_FACTORY变量指定的工厂类,调用其getInitialContext方法生成上下文。

⑨  经过上面8个步骤,已经把真正执行bind跟lookup的上下文实例确定出来了,此时如果调用bind方法就会间接调用getStateToBind把即将被绑定的对象进行转换成JNDI鼓励的存储类型,而如果调用lookup方法则会间接调用getObjectInstance把JNDI鼓励的存储类型数据转化为Java程序使用的Java对象。

⑩  调用bind时,NamingManager.getStateToBind(Object obj, Name name, ContextnameCtx,Hashtable<?,?> environment)根据环境尝试获取StateFactory,如果设置了StateFactory则使用这个工厂的getStateToBind方法实现具体转化策略。

⑪  调用lookup时,NamingManager.getObjectInstance(Object refInfo,Name name, Context nameCtx,Hashtable<?,?> environment)根据refInfo对象的getFactoryClassName方法得到资源的工厂类,再由这个工厂类的getObjectInstance方法实现具体转化策略。例如Tocmat中,用ResourceRef作为JNDI鼓励的存储类型,当把一个ResourceRef对象传进NamingManager.getObjectInstance方法中,将会调用ResourceRef对象指定的资源工厂类ResourceFactory的getObjectInstance方法生成Java对象。

总的来说优先级最高的是InitialContextFactorybuilder,如果存在则直接根据builder返回上下文了,其它工厂类相关变量失效,例如Context.INITIAL_CONTEXT_FACTORY和Context.URL_PKG_PREFIXES;接着根据有无scheme分别利用Context.URL_PKG_PREFIXES和Context.INITIAL_CONTEXT_FACTORY变量指定的工厂类创建上下文。

在了解了以上JNDI运行机制再看看下面我们常见程序,其实就是JNDI的使用,先设置变量,再传进InitialContext进行实例化,最后获取数据源。根据上面对jndi框架的剖析,从下面三段代码你能想象到内部的运行逻辑是怎样的了吗?他们之间分别有什么不同的呢?

Hashtable<String, String> env = new Hashtable<String,String>();

env.put(Context.INITIAL_CONTEXT_FACTORY,"org.apache.naming.factory. DataSourceLinkFactory");

env.put(Context.URL_PKG_PREFIXES,"org.apache.naming");

Context context = new InitialContext(env);

DataSource ds = (DataSource)context.lookup("jdbc/MyDB");

Context context = new InitialContext();

DataSource ds = (DataSource)context.lookup("java:comp/evn/jdbc/myDB");

Hashtable<String, String> env = new Hashtable<String,String>();

NamingManager.setInitialContextFactoryBuilder(new XxxInitialContextFactoryBuilder());

Context context = new InitialContext(env);

喜欢研究java的同学可以交个朋友,下面是本人的微信号:

Java命名和目录接口——JNDI的更多相关文章

  1. Java Naming and Directory Interface (JNDI) Java 命名和目录接口

    https://www.oracle.com/technetwork/java/jndi/index.html Lesson: Overview of JNDI (The Java™ Tutorial ...

  2. JNDI(Java Naming and Directory Interface,Java命名和目录接口)

    JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访 ...

  3. spring配置JNDI(Java Naming and Directory Interface,Java命名和目录接口)数据源

    1.在tomcat下的server.xml的 <GlobalNamingResources> </GlobalNamingResources>添加下面代码 <Resour ...

  4. JNDI Java 命名与目录接口

    jsp <% Context ctx = new InitialContext(); String jndiName = (String) ctx.lookup("java:comp/ ...

  5. 命名和目录接口 JNDI-The Java Naming and Directory Interface

    命名和目录接口 JNDI-The Java Naming and Directory Interface JNDI (The Java Naming and Directory Interface)为 ...

  6. JNDI--Java命名和目录接口

    JNDI主要用于在容器中配置某些资源,让所有项目可以使用.JNDI可以提供: 1:数据库连接池.            自定义连接池             第三方连接池 Dbcp           ...

  7. JNDI—目录接口名

    1:什么是JNDI? Java名称与目录接口:java Naming and Directory Interface未开发人员提供的查找和访问各种名称和目录的 服务和接口 2:全局的上下文配置文件: ...

  8. [JAVA第二课] java命名规则

    Java良好的命名规则以及代码风格可以看出来一个程序员的功底,好多公司也会注重这方面,他们招聘员工在有些时候往往就是根据一个人的代码风格来招人,所以下面就就我知道的代码风格作简要的说明一下.Java命 ...

  9. java和Jvm目录

    回到占占推荐博客索引 主要介绍java基础知识,非框架类及JVM相关的内容文章 java和Jvm目录 Java~关于开发工具和包包 Java~类,抽象类和接口 Java~时间戳小知识 Java~命名规 ...

随机推荐

  1. python 网路爬虫(二) 爬取淘宝里的手机报价并以价格排序

    今天要写的是之前写过的一个程序,然后把它整理下,巩固下知识点,并对之前的代码进行一些改进. 今天要爬取的是淘宝里的关于手机的报价的信息,并按照自己想要价格来筛选. 要是有什么问题希望大佬能指出我的错误 ...

  2. 线程基础(CLR via C#)

    1.线程基础  1.1.线程职责  线程的职责是对CPU进行虚拟化.Windows 为每个进程豆提供了该进程专用的线程(功能相当于一个CPU).应用程序的代码进入死循环,于那个代码关联的进程会&quo ...

  3. 亲戚(relative)

    [题目背景] Y 家是世界上最大的家族,HJZ 是其中一员. 现在 Y 家人想要拍一张全家福,却发现这是一个十分复杂的问题. . . . . . [题目描述] Y 家一共有 n 人 其中每个人最多有一 ...

  4. slab机制

    1.内部碎片和外部碎片 外部碎片 什么是外部碎片呢?我们通过一个图来解释: 假设这是一段连续的页框,阴影部分表示已经被使用的页框,现在需要申请一个连续的5个页框.这个时候,在这段内存上不能找到连续的5 ...

  5. 【BZOJ4034】【HAOI2015】树上操作

    题目请自行查阅传送门. 典型的树剖题,线段树维护操作,记一下子树在线段树内范围即可. 时间复杂度:\( O(m \log^{2} n) \) #include <stdio.h> #def ...

  6. [洛谷]P3613 睡觉困难综合征

    题目大意:给出一棵n个点的树,每个点有一个运算符(与.或.异或)和一个数,支持两种操作,第一种修改一个点的运算符和数,第二种给出x,y,z,询问若有一个0~z之间的数从点x走到点y(简单路径),并且对 ...

  7. DeepMoji:机器学习模型分析情绪, 情感

    DeepMoji 是一个模型,接受12亿个带有表情的推文,以了解语言如何表达情绪. 通过转移学习,该模型可以在许多情感相关的文本建模任务上获得最先进的表现. 在 http://deepmoji.mit ...

  8. 最新版-MySQL8.0 安装 - 改密码 之坑

    1. 需求背景 最近需要在一台性能一般的电脑上使用数据库,所以决定安装MySQL数据库,以前安装都是使用WorkBench自动化安装,但安装过程太慢占用空间过大,于是下载zip压缩包.之所以选择选择M ...

  9. 利用css3+js实现简单带立体过渡效果的图片切换(chrome浏览器)

    <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...

  10. 解决com.fasterxml.jackson.databind.JsonMappingException: No suitable

    原因:直接翻译,json格式,不匹配. 这原因坑爹啊,因为json格式不正确算一种原因. 还有一种就是接收的bean没有getter,setter方法. 最坑的一种就是数据无法被反序列化,list,m ...