JNDI 学习笔记

首先我们来回顾一下简单的问题,列在下面第一点。 
1.我们知道,Java 的运行从 static main 开始,为什么一定要从 static 方法开始呢? 
2.在我们知道这个世界上的另外一个地方有一个对象存在而且服务器也会在我们开始工作前为我们准备好,那么我该怎么找到它呢?如果这个对象是我这个类创建的,那么当然简单,直接用对象的引用就能调用它的方法,那如果这个对象不是我创建的,我想主动调用它的方法这似乎在任何编程语言中都不可能,记得写一个方法那是被别人调用的不是主动调用别人。 

就像你找人一样,如果他还没有和你建立联系的话(建立联系就是保存一份对象的引用),如果两个对象彼此没有创建与另外一个对象的联系,而且也没有被中间的第三方建立这种关系问题就出现了,请问你如何和他打交道? 
现实中是: 
a. 我们拨通 114
      Java 中:Context ctx = new InitialContext(); // 通过第三方建立联系。
b. 请问哪里有通马桶的?114 答,xxx... 为您转接中,请稍候。      
      Java 中:DataSource ds = (DataSource) ctx.lookup("便民服务公司");//得到联系人或技术员(一个 DataSource 对象)。
c. 过了一会儿,人来了,你说:师付,请帮我通马桶吧。 
      Java 中: ds.getConnection(); // 已经联系成功了,可以处理业务了。

上面的话,我没有回答你什么是 JNDI, 但是我回答了为什么我们需要 JNDI. 希望你在概念上了解了它存在的必要性。 

下面的话,给你一点指导如何更好的理解 JNDI 实现: 
1.一个对象如果它在另外一个地方(可能与当前运行的程序不在同一个 VM / 同一进程中), 对象怎么可能从一个 VM 中发送到另外一个 VM 中呢?像 LDAP 这种,对象的状态还需要持久地保存的话(重启服务器进程后它还在),又该怎么办呢?请看 JNDI StateFactory, 它用一种方法把一个对象转换成某种方式保存下来,就像我们把一个 Entity 对象保存下来时,我们会用 insert/update SQL 来做一样,把一个 Entity 转换成一条 insert/update SQL 的类就在概念上能称为 StateFactory 了。

2.有一个对象上次已经保存了状态,现在服务器重启了,上次的对象肯定不在内存里面,我们怎么恢复上次的状态呢? 
请看 JNDI ObjectFactory. 它读取一些上次保存的状态信息,来创建并初始化一个对象。比如:我们配置了一个 XML,它是某个 JDBC 数据源的配置数据,Application Server 启动时读取这个信息(相当于上次的状态),然后重建对象状态(在这里就是创建 Data Source 对象本身)。 

3.企业应用这么复杂,面向接口编程,那如何用一种简单的方式来配置新的实现类呢?Java 的做法是: 
已经定义了 SPI (Service Provider Interface). 包括以下几点: 
  接口准备好了,如:StateFactory / ObjectFactory. 
  配置:先搜索 JRE 下面的某个 jndiprovider.properties 文件当作默认实现,再查找用户 classpath 根路径下 /jndi.properties. 另外还有 System.getProperties() 和在创建 InitialContext 给一个 hashtable 作为参数,这三个参数, 有优先级的关系,越是后面具体的参数优先级越高,越前面越通用型的参数优先级越低。这一点,请看 JDK ResourceManager 这个类的源码。 
  实现类与初始化它们是如何自动完成的呢?这个你需要看 Context 接口里面的常量,以及拿 Sun LDAP InitialContextFactory 运行样例来看 Context 接口的常量一个样本参数值,一般我们很重要的是 InitialContextFactory 这个参数,但也有时候也有其他参数要配置,比如:pkgs, 它是说,我们给一个包名,JNDI 管理器要查找实现时用这个包名列表当成包名,类名就是 协议名 + 固定的后缀:比如: ldap://localhost:389, 它会用一个'包名前缀.协议名.协议名 + URLContextFactory' 作为类名来搜索一个类,如果它存在就把它当成实现类,如果没找到再尝试另外一个包名前缀。你可以看 com.sun.jndi.url 名,下面有例子看,比如说 ldap:// 的情况就是 找一个类 com.sun.jndi.url.ldap.ldapURLContextFactory,如果是 dns://www.163.com/xxx 就找个 com.sun.jndi.url.dns.dnsURLContextFactory。这是 URL context factory 也就是当你使用 ctx.lookup("java:xxxx/yyy") 这种带协议前缀的时候。 

另外你也可以类比地看 com.sun.www.protocol 包里面的类,它是另外一个与 JNDI 不相关的 URLStreamHandler 处理的规则,与些设计和配置几乎完全相同。我以前写过一个 jdbc:oracle:username/password:@localhost:1521:training/[select A from C where DEL_IND = 0] , 在 java 程序中输入这个 URL 我们可以把数据库里面的数据读取出来,效果就根你输入 file:/C:/boot.ini 读取了这个文件内容一样,办法就是我写了一个支持 jdbc 协议的 URLStreamHandler 在命令行配置一个使用它,其他的应用程序类就能自动处理,(我不用传递任何参数给应用程序) 它们都不知道我是从数据库里面读取的数据。 

4. J2EE 1.3 开始,资源的管理由应用服务器单独来管理和配置,这与 J2EE 1.2 不同,在 J2EE 1.2 中我们直接在应用程序中配置我们要用的资源。J2EE 1.3 中我们配置一个数据源在服务器上,我们在应用程序中只需要说明我们配置的资源的引用就行了,比如我们只在 web.xml 或 ejb-jar.xml 配置 <resource-ref /> 而不是 data source 本身。这有什么好处?比如:我们定义了两个 training 的数据源:jdbc/training/db2. jdbc/oracle/db2. 一个是开发环境,一个是 UAT 环境,现在开发时我们建立一个 <resource-ref /> 指向jdbc/training/db2,那么就用 db2 数据库,UAT 测试时我们建立另外一个 <resource-ref /> 指向 jdbc/training/oracle, 就会使用 oracle 数据库,只是修改了 web.xml / ejb-jar.xml ,而且现在连接到数据的用户名和密码不再是应用程序开发本身的事情,因为你不需要配置资源也就不需要知道它的登录名和密码,而是由管理员在服务器上配置数据源,这里注意,开发人员做他代码部分的事情,服务器管理员负责配置资源源,J2EE component provider 和 Deployer 两个角色的职责分开了,虽然现实中 deployer 都是委托给了开发人员,但 J2EE 规范是分开来描述的。打开一份 Tomcat Server.xml 安装自带的默认配置文件来看,里面有不少被注释掉的样例配置作参考,其中包括了几种 JNDI 用到东西,比如资源(<resource /> 标签) 资源引用 ( <resource-Link /> 标签),这也是学习的另一个方法。

5.上面说了半天,目的是什么呢?这是我的痛苦经历,第一次写 EJB, 买了本书,J2EE 从入门到精通(就是那本传说中的黃皮宝典系列),写了一个无状态 session bean 来访问数据源,死活找不到数据源:NamingException: xxx not found. 在 IBM developerworks 上看到一篇文章,茅塞顿开,原来那本书讲的是J2EE 1.2, 我用的 WSAD 5.1.2 开发用的默认配置都是 J2EE 1.3。这里面引出了 JNDI LinkRef, 为了实现上面 4 里面所说的服务器上配置一个资源,但应用程序里面配置一个引用的话,现在的应用服务器在处理这点JNDI技术实现上基本上都是用 LinkRef 来实现的,这是 JNDI 里面的一个类。服务器启动时会创建一个 jdbc/training/db2 和 jdbc/training/oracle 两个 DataSource 对象 (用的是 ObjectFactory), 当一个应用程序启动时,服务器检测到了 web.xml/ejb-jar.xml 中指定了 <resource-ref /> 它就会创建一个 LinkRef 放到 context 中去,它的名称是:jdbc/training,但它的 ref 是 jdbc/training/db2. 
这样我们 ctx.lookup("java:jdbc/training") 时,java 协议对应的 javaURLContextFactory 会把这个 jdbc/training 对象找出来,在检测到它是一个 LinkRef 对象时,会自动再用它的 ref 值(这里是 jdbc/training/db2) 再 lookup 一遍,这下终于找到 jdbc/training/db2 这个 data source 对象。 

6.JNDI 里面还有其他的相关的东西。再结合一个 Reference 概念看,LinkRef 是继承它的。想再具体的了解一个实现细节,请拿一份 apache commons-xxx.jar (名字我忘记了,不过用过 spring / hibernate 来创建数据源的人可能知道它们用 xxxDataSource 做一个不需要在服务器上配置,但却能使用 data source 的办法),我不是推荐你这个 jar, 我是推荐你看这个xxxDataSource 源码,里面演示了一个 ObjectFactory 用法。这和 JMS ConnectionFactory等其他 J2EE 托管资源的配置和使用都是用的同样的技术实现的。举一反三。 
想了解更,就再看一个 StateFactory 的实现以及 Reference 的源码之类的。我们至少在概念和理论上要知道 ObjectFactory / LinkRef / SPI / resource ref 配置这几点,如果你再知道 StateFactory 是怎么实现的就更好了。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值