JNDI 是JAVAEE 平台技术中最重要的基础支撑技术之一。他为各种Java对象进行命名,并采用目录层次结构管理它们,借助于JNDI API能够操控各种JAVA对象。实际上,JAVAEE5 引入各种Annotation注释(比如,@EJB,@Resource)底层采用JNDI API查找 JavaEE 容器受管资源(比如,EJB3.0组件,JDBC DataSource,JMS ConnectionFactory),并注入到那些应用了Annotation注释的JavaEE组件 (比如,Servlet,JSF后端Bean,EJB3.0组件)中。
Spring2.0针对JNDI API的使用提供了JNDI集成,org.springframework.jndi包就是见证。在Spring 1.x使能应用中,JndiObjectFactoryBean是Spring开发者使用最频繁的工厂Bean,而JndiTemplate模板的使用也 非常频繁。自从Spirng 2.0开始,<jndi-lookup/>内容模式被引入到JNDI集成工作中。此后,<jidi-lookup/>便代替了 JnidObjectFactoryBean的地位,当然JndiObjectFactoryBean是<jndi-lookup/>底层采 用的工厂Bean。
本章从介绍JNDI背景知识及实例入手,进而进入到Spring提供的JNDI集成支持中。随后,Spring2.0引入的<jndi-lookup/>内容模式被介绍到。
背景知识及示例
现有的企业当中,存在各式各样的命名和目录服务。何谓命名和目录服务?命名和目录服务是同过名字,目录形式来管理系统中的对象,无力设备等内容的一种服 务。比如,微软活动目录(AD)、OpenDAP服务器。为了通过应用操纵它们,开发者需要借助于响应的客户端API或客户软件
在JNDI出现之前,开发者只能够通过专有方式操作命名和目录服务,这无疑加大了开发者的学习负担。甚至,企业的实现环境都是异构的,哪怕是同一种目录服务器都会因为运行的操作系统的不同而提供多套客户端API。
JNDI,即Java命名目录服务接口(Java Naming and Directory Interface),它能够对命名和目录服务器进行CRUD操作,这同JDBC与RDBMS的关系类似。只要命名和目录服务器厂商提供对应的JNDI实 现,开发者便能够一致地通过接口操纵它们,从而避免了与厂商绑定的风险。
类似于DNS,JNDI能够讲对象赋予有意义的名字。在DNS中,通过逻辑能够实现对IP地址的映射。在JNDI中,通过预先绑定的名字能够找到目标服务 或者对象。比如,借助于JNDI,我们能通过“java.:/AgileSpringDS”名字获得javax.sql.DataSource对象。这些 都是通过Java命名系统获得的。JNDI在Java SE/Java EE中起到了很重要的作用,正如DNS在当今的因特网中的地位一样,而且会越来越重要。比如,Web服务的兴起、SOA架构的兴起,会使得JNDI在应用 中扮演关键角色。
通常,Java EE应用服务器(比如,GlassFish,JBoss)都会提供命名或目录服务实现(或称为服务提供者)。BEA WebLogic内部采用了LDAP服务器完成登录用户的认证。同样地,在Java SE环境开发中也可以使用本地JNDI服务提供者(而不是由Java EE服务器提供的)。通过http://java.sun.com/products/jidi/serviceproviders.html,开发者能 够浏览到很多JNDI服务提供者。这里,我们以 File System提供者为例。如果开发者熟悉IBM WebSphere MQ,则应该知道他的JNDI实现这是在File System基础之上开发的。打开Eclipse filesystemjndidemo项目,来分析如下代码。
- Hashtable<String,String> env = new Hashtable<String,string>(2);
- //设定上下文工厂
- Env.put(Context.INITIAL_CONTEXT_FACTORY,”com.sun.jndi.fscontext.RefFSContextFactroy”);
- //设置文件系统路径(注意,开发者需要依据自身的机器修改”file:d://eclipse”)
- Env.put(Context.PROVIDER_URL,”file:d://eclipse”);
- Context initCtx = null;
- Try{
- //获得初始化上下文
- initCtx = new initialContext(env);
- //借助于JNDI,查找文件
- Object fc =initCtx.lookup(“eclipse.ini”);
- Log.info(fc);
- }
- Catch(NamingException ne){
- //处理命名异常
- Log.error(“NamingException”,ne);
- }
- Finally{
- Try{
- //关闭上下文
- initCtx.close();
- }catch(NamingException ne){;}
- }
上述代码先构造env,然后传给InitialContext.此时,”file:d://eclipse”成了JNDI树的根,eclipse目 录下的所有文件和目录都成为了树的成员。在查找到eclipse.ini后,需要关闭相应的InitialContext。整个过程需要处理 NamingException异常。
用户可以将应用对象存储在命名和目录服务提供者中,并供JNDI应用访问。比如,开发者可以将数据源存储在命名和目录服务器中;通过JNDI能够获得JTA事务管理其(存储在JavaEE 应用服务器提供的命名和目录服务器中)。
实例仅仅展示了JNDI的冰山一角。在实际企业应用中,JNDI的使用更多地发生于Web,EJB,Ear应用场合。比如,在JSF受管组件中访 问EJB3.0组件,借助于JNDI访问JMS Topic及ConnectionFactory。在这些场合中,JNDI的使用非常复杂。如果直接通过JNDI API操控目标资源,开发者要处理很多与实际业务无关的开发内容,具体如下:第一,要负责InitialContext的创建、关闭工作;第二,要负责异 常处理;第三,要考虑JNDI树上查找目标资源的实际;第四,查找到目标资源后,开发者还要考虑缓存策略(运行时是否需要重新去JNDI树上获取资源,否 则他会持有过期资源);第五,要区别对待开发时和生产环境的JNDI处理。考虑到这些对开发者的不利因素,Spring对JNDI提供了全方位的支持,这 也是Spring驱动敏捷开发的又一举措。
通过将JNDI查找操作定义在Spring配置文件中,应用能够操作到相关的JNDI对象,开发者再也不用对JNDI进行硬编码。最为重要的一 点是,应用对这些JNDI对象的依赖都是通过IoC容器注入的,因此开发者不用关注具体对象是从哪里来。甚至,Spring2.0对于Java EE资源(包括JNDI)的配置做了许多简化工作,比如引入<jee:jndi-lookup/>内容模式。