JNDI简介
程序员文章站
2022-06-07 16:29:23
...
一. JNDI基础
1.1 JNDI
JNDI(Java Naming and Directory Interface)是Java命名和目录接口的简称,是J2EE的核心技术之一。在JMS、JMail、JDBC、EJB等技术中都大量应用了JNDI技术。
1.2 命名服务
命名是计算机系统中最基本的一个部分。人们给予实物一个名字,然后通过这个名字找到实物。例如,当你发送电子邮件的时候,你必须知道收件人的地址;当你在文件系统中查找一个文件的时候,你必须知道文件的名字。命名服务就是这样一个东西,它可以让你根据名字找到一个实物。
命名服务的主要功能就是能友好地把名字和实物映射到一起。比如,我们所熟悉的DNS系统,它就会把www.sun.com映射成IP地址192.9.48.5。再比如一个文件系统,我们可以通过名字C:\Windows\regedit.exe访问到其所对应的文件。这两个例子说明命名系统是广泛存在和被使用的,无论是在Internet还是单机系统。
1.2.1 命名方式
为了在一个命名系统里找到某一个对象,你必须提供这个对象的名字才可以。这个命名系统决定了名字的格式应该是什么样子的。这种名字的格式就称为命名方式。
例如:
Unix文件系统的命名方式是相对根目录的文件名,每一级目录都必须以“/”来分隔开来。例如“/usr/hello”就表示一个在“/usr”目录里的名字叫“hello”的文件。
DNS系统的命名方式是不同的部分用“.”分隔开来。因此DNS名字“sales.Wiz.com”就表示了一个DNS项,名字是“sales”。但请记住它是相对“Wiz.com”来说的。同样,“Wiz”是相对“com”的一个名字。
LDAP的命名习惯则是从右向左,由“,”分隔。因此LDAP名字“cn=lee,o=sun,c=us”就命名了一个名字是“cn=lee”的项,当然,它是相对“o=sun,c=us”来说的。同样,“o=sun”是相对“c=us”的一个名字。而且LDAP还有一个规则,就是每个名字必须是“键值对”对应的,键值之间用“=”分开。
1.2.2 绑定(Binding)
把一个名字和一个实际的对象联系在一起就称为绑定。例如,一个文件名和一个文件绑定在一起,一个域名和一个IP地址绑定在一起,一个LDAP名字和一个LDAP项绑定在一起。
1.2.3 引用和地址
有些对象是不可能直接保存在命名系统里的,也就是说,我们不能把某些对象复制到命名系统里。但是命名系统可以保存一个指向对象的引用,就好像一个指针。一个引用只是告诉命名系统如何去找到一个被引用的对象。一个引用里不会包含很多信息,因为你可以从对象那里得到详细的信息。
例如,一个“飞机”可能保存这样一些信息:乘客信息、驾驶员信息、飞行计划等等。但是,一个对这个“飞机”的引用,可能只包含航班号和到达时间。对于一个文件对象的引用来说,可能只包含文件的名字;对于一个打印机对象的引用来说,可能只包含打印机的位置和所使用的通信协议。
尽管对于一个对象的引用中可以包含任意的信息,但是我们最好还是确定一些该如何才能找到一个对象,这就是所谓的地址。
1.2.4 内容上下文(Context)
内容上下文其实就是一系列的“名字-对象”的绑定。每一个内容上下文都有对应的命名规则。一个内容上下文都会提供一个“lookup”方法去查找一个对象,或许还会提供一些其它的方法:绑定新的“名字-对象”、解除一个绑定、列出全部“名字-对象”等等。如果一个内容上下文里的某个名字对应的对象还是一个内容上下文,那么这个名字对应的内容上下文可以被称为“子内容上下文(SubContext)”。
例如,在Unix系统里,“/usr”就是一个内容上下文,“/usr/bin”是其子内容上下文;在域名系统中,“com”是一个内容上下文,那么对于“sun.com”来说,“sun”就是“com”的子内容上下文。
1.2.5 命名系统和命名空间
一个命名系统就是一系列具有相同命名方式并提供一些相同操作的系统。例如,DNS就是一个命名系统,LDAP也是一个命名系统。一个命名系统提供了命名服务和基于名字的一些操作。命名服务通过它的接口来访问。例如,DNS提供了映射域名和IP的命名服务,文件系统提供了文件名字映射文件的服务。
命名空间就是一系列的命名系统的集合。例如,Unix系统的命名空间是由全部的文件和目录所组成的。
1.3 目录服务
许多命名服务被扩展成了目录服务。目录服务不仅用名字和对象作为联系,而且使用属性和对象进行联系。因此,你不仅可以通过名字来找到一个对象,你还可以通过属性来找到一个对象。
电话公司的目录服务就是一个很好的例子。电话公司的系统把一个用户的名字和电话号码、地址联系在一起。一个目录服务系统就像电话公司的一样——电话号码、地址,其实都是一个对象的属性。一个目录对象可以是一个打印机、一个人、一台电脑,甚至是一个网络。一个目录对象会包含它所必须的属性。
1.3.1 属性
一个目录对象可以具有属性,例如一个打印机可以具有型号、颜色等属性,一个用户对象可以具有电话号码、邮件地址等属性。属性包含一个属性名称和许多属性值。目录服务包含了一系列的目录对象,目录服务提供了许多方法来对属性进行操作,例如创建、删除等等。
1.3.2 查询和过滤
你可以通过给目录服务系统提供一个名字来查询一个目录对象。当然,对于一些目录服务系统,例如LDAP,比较主张使用查询语句来得到目录对象。使用查询语句查询对象的时候,你不仅可以指定一个目录的名字,还可以指定目录的属性,甚至是可以使用一些查询表达式。这种使用表达式的方式,就是过滤查询。这种方式有时候也被称为“反向查询”或者“基于内容的”查询。例如,你可以从目录系统里查询所有年龄大于40岁的用户;你也可以查询所有IP地址以“192.113.50”开头的机器。
1.3.3 结合命名和目录服务
目录服务通常会以层次的方式组织目录对象。例如,LDAP把所有对象组织成一个树的形式,被称为Directory Information Tree(DIT)。例如,一个“组织”对象,可能会包含一个“组”对象,而一个“组”对象可能会包含一个“人”对象。当目录对象以这种方式组织在一起的时候,除了可以被当作属性的容器以外,它们也可以被看作是命名服务对象了。更简单点说,命名服务提供了一种层次的服务,而目录服务提供了一种属性的服务。我们一定要区分它们的概念。从实际应用来说,我们可能只使用命名服务,而可以不必使用目录服务。但是如果我们使用了目录服务,那么一般都是要同时使用命名服务的。
1.4 具有目录服务功能的Java应用
目录服务是计算机网络的一个重要组成部分。通过使用目录,你可以简化应用和对应用的管理。随着Java应用的日趋广泛,掌握访问目录服务的能力就很必要了。
1.4.1 目录服务的传统使用方式
一个具有目录服务功能的应用,要么使用了命名服务,要么使用了目录服务。具有目录服务功能的应用和Applets,和其他应用一样,可以用传统的方式访问目录服务,也就是说,可以保存或者查询目录对象。一个Java邮件客户端,可以把一个目录服务作为邮件地址簿,可以从中查询邮件地址;一个邮件传输的代理程序,可以从中得到路由的信息;一个日历程序,可以从中得到用户的设置信息。
各种应用程序可以通过目录服务得到公用的基础信息。这种共享方式,可以让应用跨系统部署,设置是跨网络部署,并且让这些部署的耦合更加清晰,而且便于管理。例如,打印机配置和邮件路由信息都可以保存在目录服务里,这样,这些配置就可以被所有需要打印机和邮件路由的信息的应用所调用。
1.4.2 把对象保存在目录服务里
我们不仅可以按照传统方式使用目录服务,Java应用也可以把目录服务作为一个对象的仓库来使用。例如,一个应用可以从目录服务里得到一个打印机对象,然后发送一些信息给打印机对象让它打印出来。
二. JNDI架构
JNDI的架构与JDBC的架构非常类似。JNDI架构提供了一组标准命名系统的API,这个基础API构建在中间层之上,这个中间层称为命名管理层,其功能由JNDI SPI来完成。
JNDI API提供如下五个包(其中前四个包定义了JNDI客户端接口,面向使用命名服务和目录服务的应用;最后一个包定义了JNDI服务提供者接口,即SPI,面向不同的命名和目录服务提供者):
Javax.naming
Javax.naming.directory
Javax.naming.event
Javax.naming.ldap
Javax.naming.spi
在应用程序中,我们实际上使用的就是以上几个包中的类,具体调用类及通信过程对用户来说是透明的。
JNDI提供了访问不同JNDI服务的一个标准的统一的客户端接口,通过不同的访问提供者接口(JNDI SPI)的实现,由Service Provider将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。SPI的具体实现包括了几个增强和下面的命名/目录服务提供者:
LDAP(Lightweight Directory Access Protocol) 服务提供者
CORBA COS(Common Object Request Architecture Common Object Service)命名服务提供者
RMI(Java Remote Method Invocation)注册服务提供者
DNS(Domain Name System)服务提供者
FSSP(File System Service Provider)文件系统服务提供者
其它服务提供者
总结:JNDI API只是作为应用程序客户端的实现,其服务端是由SPI对应的公司/厂商来实现的,我们只需要将服务端的相关参数传送给JNDI API即可,具体调用过程由SPI来完成。
三. 命名服务API
Package:javax.naming
3.1 Context接口和InitialContext类
Context是命名服务的核心接口,提供对象查找、绑定/解除绑定、重命名对象、创建和销毁子上下文等操作。
InitialContext类实现了Context接口,是访问命名服务的起始上下文,通过它可以查找对象和子上下文。
Context主要方法如下:
根据名称获取对象
Object lookup(Name name)
Object lookup(String name)
绑定名称到对象
void bind(Name name, Object obj)
void bind(String name, Object obj)
解除绑定,释放对象
void unbind(Name name)
void unbind(String name)
将对象和一个已经存在的名称重新绑定
void rebind(Name name, Object obj)
void rebind(String name, Object obj)
修改对象名称
void rename(Name oldName, Name newName)
void rename(String oldName, String newName)
列出上下文中的所有对象名称信息。NameClassPair包含对象名称和对象类名。
NamingEnumeration<NameClassPair> list(Name name)
NamingEnumeration<NameClassPair> list(String name)
列出上下文中的所有绑定
NamingEnumeration<Binding> listBindings(Name name)
NamingEnumeration<Binding> listBindings(String name)
创建子上下文
void createSubcontext(Name name)
void createSubcontext(String name)
销毁子上下文
void destorySubcontext(Name name)
void destorySubcontext(String name)
3.2 Name接口
对应于命名服务概念中的对象名称。它的具体实现可能是一个简单的字符串,也可能是一个复杂的对象。CompoundName类和CompositeName类均实现了Name接口,分别代表复合名称和混合名称。
3.3 Binding类
对应于命名服务概念中的绑定。一个Binding包含对象名称、对象的类名称、对象本身。
Binding主要方法如下:
获取/设置对象名称
String getName()
void setName(String name)
获取/设置对象类名
String getClassName()
void setClassName(String name)
获取/设置对象
Object getObject()
void setObject(Object obj)
3.4 Referenceable接口和Reference类
命名服务中的对象的存储方式各不相同。有的将对象直接序列化,这时实现标准的Serializable接口。有的要将对象存储在命名系统外部,这就要用到Referenceable接口和Reference类了。Reference类包含了怎样构造出一个实际对象的信息,实际对象则需要实现Referenceable接口。
Referenceable主要方法如下:
返回对象的引用
Reference getReference()
当将一个实现了Referenceable接口的对象绑定到Context时,实际上通过getReference()得到它的Reference进行绑定。而如何从Reference中创建出Referenceable的实例,则由具体的SPI实现,JNDI客户不必关心。
四. 目录服务API
Package:javax.naming.directory
4.1 DirContext接口和InitialDirContext类
DirContext是目录服务的核心接口,它扩展了Context接口,除了提供命名服务的各种操作外,还提供了访问和更新目录对象属性的操作,以及Search操作。
InitialDirContext类扩展InitialContext并实现了DirContext接口,是访问目录服务的起始点。
DirContext主要方法如下:
bind/rebind/unbind等方法与Context类似,区别是各个方法中均添加了Attributes参数,表示绑定的是一个目录对象,其中有对象本身,还有对象的属性集合。这里不再列举。
获取对象的属性集合
Attributes getAttributes(Name name)
修改对象的属性集合
void modifyAttributes(Name name, int mod_op, Attributes attrs)
搜索包含匹配的属性的对象
NamingEnumeration<SearchResult> search(Name name, Attributes matchingAttrs)
通过查询过滤条件进行操作,同时指定了搜索控制
NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons)
4.2 Attribute接口和Attributes接口
Attribute接口对应于目录服务概念中的属性,Attributes表示属性的集合。
4.3 SearchResult类和SearchControls类
SearchResult类继承自Binding类,表示DirContext的search操作结果。SearchControls类用于对搜索操作进行更精细的控制,如指定搜索范围(Scope)、时间限制(TimeLimit)和结果数量限制(CountLimit)。
五. 服务事件API
Package:javax.naming.event
5.1 EventContext接口和EventDirContext接口
分别表示支持事件通知的上下文,提供了添加和删除时间监听器的操作。
5.2 NamingEvent类
命名和目录服务产生的事件。包含一个type,表示不同的事件类型。
5.3 NamingListener
NamingListener是处理NamingEvent事件监听器的接口。
5.4 NamespaceChangeListener
NamespaceChangeListener是NamingListener的子接口。
5.5 ObjectChangeListener
ObjectChangeListener是NamingListener的子接口。
参考文档见附件