1. 什么是JNDI?

JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。

对于JNDI的概念不少同学表示,一看就觉得头疼,根本没有想耐下心看的兴趣。为此,博主将上面复杂的概念拆分成一些简单的概念要点,帮助大家更好地理解JNDI的概念:

  1. JNDI本质上是一系列通用的标准的用来操作命名服务和目录服务的接口: 换句话说,这一系列的接口就是对操作所有命名服务和目录服务的一种超级抽象,有了这些接口我们就可以用通用的接口操作各式各样的命名服务或者是目录服务,而不需要每次与一种服务交互就写一次操作该服务的代码,始终记住一个宗旨,以抽象应对改变。
  2. JNDI的这些通用接口需要依靠服务供应接口(SPI)的实现来将通用的接口映射为特定的命名或目录服务,使客户端能够和指定的服务进行交互:这句话的意思就是尽管虽然提供了一系列超级抽象版的通用接口API供客户端使用,但是使用这些接口的人该怎么知道用这些接口是在访问一个文件目录服务还是一个DNS命名服务呢?因此JNDI又提供了一系列的服务提供者接口(SPI),所有的服务供应商只要根据这些SPI去提供自己服务的实现,客户在使用通用接口时引入这些SPI的实现类,就可以操作指定的服务了。

2. 命名服务与目录服务

JNDI的目的就是对命名服务(Naming Service)与目录服务(Directory)中的资源进行查找或者管理,那么什么是命名服务,什么又是目录服务呢?

命名服务的概念其实很好理解,在生活中也有很多的例子,最典型的就是DNS(Domain Naming Service)了。众所周知,DNS是用来对人类更便于记忆的域名和计算机更便于记忆的IP地址进行映射的。因此命名服务的核心就在于映射,讲一个值映射成另一个值。

至于目录服务呢,它其实是命名服务的一种自然扩展,两者之间的关键差别是目录服务中对象可以有属性,而命名服务中对象没有属性。举个简单的例子吧,我们手机中的电话簿就是一个典型的目录服务。它将一个电话号码映射成了拥有这个号码的人,但是人这个对象不仅仅会有姓名(尽管姓名最重要),这也就是为什么电话簿中还可以增加这个人的家庭住址,公司,电子邮件,电话铃声等等。

3. JNDI架构

e7a6008a18a547a8a38b310e689a0221-JNDI.jpg

JNDI架构提供了一组标准的独立于命名系统的API,这些API构建在与命名系统有关的驱动之上。这一层有助于将应用与实际数据源分离,因此不管应用访问的是LDAP、RMI、DNS、还是其他的目录服务。换句话说,JNDI独立于目录服务的具体实现,只要有目录的服务提供接口(或驱动),就可以使用目录。

关于JNDI要注意的重要一点是,它提供了应用编程接口(application programming interface,API)和服务提供者接口(service provider interface,SPI)。这一点的真正含义是,要让应用与命名服务或目录服务交互,必须有这个服务的JNDI服务提供者,这正是JNDI SPI发挥作用的地方。服务提供者基本上是一组类,这些类为各种具体的命名和目录服务实现了JNDI接口—很像JDBC驱动为各种具体的数据库系统实现了JDBC接口一样。作为一个应用开发者,我们不必操心JNDI SPI的具体实现。只需要确认要使用的某一个命名或目录服务都有服务提供者。

JNDI提供了如下几个程序包:

  1. Javax.naming:包含了访问命名服务的类和接口。例如,它定义了Context接口,这是命名服务执行查询的入口。

  2. Javax.naming.directory:对命名包的扩充,提供了访问目录服务的类和接口。例如,它为属性增加了新的类,提供了表示目录上下文的DirContext接口,定义了检查和更新目录对象的属性的方法。

  3. Javax.naming.event:提供了对访问命名和目录服务时的事件通知的支持。例如,定义了NamingEvent类,这个类用来表示命名/目录服务产生的事件,定义了侦听NamingEvents的NamingListener接口。

  4. Javax.naming.ldap:这个包提供了对LDAP 版本3扩充的操作和控制的支持,通用包javax.naming.directory没有包含这些操作和控制。

  5. Javax.naming.spi:这个包提供了一个方法,通过javax.naming和有关包动态增加对访问命名和目录服务的支持。这个包是为有兴趣创建服务提供者的开发者提供的。

4. JNDI实例

正所谓光说不练假把式,下面楼主就和大家一起写一个JNDI的例子,来巩固一下上面的知识。

正如博主上面所讲,要使用JNDI,必须有服务供应商按照SPI所提供的服务实现,为了省下我们的大量时间,我们采用SUN公司官方提供的文件系统服务提供者(File System Service Provider, FSSP),这个服务提供者是用来帮助用户操作文件系统的。

首先我们需要下载FSSP的官方资源包

4f0144a25cfc435a872cd71c120598bb-FSSP.png

当然,大家也可以直接使用我的项目,但是官方包中会有一些官方的帮助文档对初次使用的朋友们还是比较有帮助的。

下面是我为大家写的一个连接了本地的文件系统的例子,当然我们需要将fscontext.jar和providerutil.jar加载到classpath下:

public class FSSPTest {

	public static void main(String[] args) throws NamingException {
		// TODO Auto-generated method stub
		Hashtable<String,String> env = new Hashtable<>();
		//指明初始化的factory是我们下载的jar包中的RefFSContextFactory
		env.put(Context.INITIALCONTEXTFACTORY, 
			    "com.sun.jndi.fscontext.RefFSContextFactory");
		//指明Context的初始URL,这里我们的是C盘
		env.put(Context.PROVIDER_URL,"file:///c:/");
		
		Context ctx = new InitialContext(env);
		
		//在C盘下创建要给文件夹名为JesminDir
		ctx.createSubcontext("JesminDir");
		
		//在C盘下定位myFile文件
		File f =  (File) ctx.lookup("myFile");
		System.out.println(f);
	
		//列出当前context下的所有元素的名称和类型(包括文件夹和文件)
		NamingEnumeration list = ctx.list("/"); 
		while (list.hasMore()) { 
		    NameClassPair nc = (NameClassPair)list.next(); 
		    System.out.println(nc); 
		}
	}
}