一、简介

JNDI(Java Naming and Directory Interface),即Java命名和目录接口,它为使用Java编写的应用程序提供命名和目录功能,被定义为独立于任何特定目录服务的实现。

JNDI允许基于Java平台的应用程序访问多个命名和目录服务;作为Java企业应用程序编程接口(API)的一部分,JNDI允许开发者创建可移植的应用程序,这些应用程序可以支持许多不同的命名和目录服务,包括:文件系统、目录服务,如轻型目录访问协议目录服务(LDAP)、Novell目录服务和网络信息系统(NIS),以及分布式对象系统,如CORBA服务(CORBA)、Java RMI服务(RMI)和EJB服务(EJB)。

二、架构

JNDI体系结构由一个API和一个服务提供者接口SPI(Service Provider Interface)组成。Java应用程序使用JNDI API访问各种命名和目录服务。SPI允许透明地插入各种命名和目录服务,从而允许使用JNDI API的Java应用程序访问这些服务。

JNDI在Java SE平台中,要使用JNDI,必须具有JNDI类和一个或多个服务提供程序。

JDK包括以下命名或目录服务的服务提供者:

  • LDAP

Lightweight Directory Access Protocol:轻型目录访问协议,它是一个通信协议,通过IP协议提供访问控制和维护分布式信息的目录信息。

  • CORBA

Common Object Request Broker Architecture:公共对象请求代理体系结构,它是一套标准,包含了相关的API以及通信协议的定义,可以使用它实现一种与WebService类似的远程调用机制。

  • RMI

Java Remote Method Invocation:Java远程方法调用。

  • DNS

Domain Name Service:域名服务。

三、JDNI Packaging

JNDI分为五个包:

  • javax.naming

  • javax.naming.directory

  • javax.naming.ldap

  • javax.naming.event

  • javax.naming.spi

1、Naming

javax.naming包包含用于访问命名服务的类和接口。此包中定义了一个Context接口,它是查找、绑定/解除绑定、重命名对象以及创建和销毁子上下文的核心接口。

  • Lookup

最常用的lookup方法,此访法根据要查找的对象的名称,返回绑定到该名称的对象。

Object lookup(Name name) throws NamingException
  • Bindings

名称与对象的关联称为绑定。JNDI中listBindings方法返回 名称-对象(name-to-object) 绑定的枚举;绑定(Binding)是一个元组,包含绑定对象的名称、对象的类的名称和对象本身。

NamingEnumeration<Binding> listBindings(Name name) throws NamingException
  • List

与listBindings类似,它返回的是包含对象名称和对象类的名称的枚举;对于浏览器这样的应用程序来说非常有用,这些应用程序希望发现关于在上下文中绑定的对象的信息,但是不需要实际对象。

NamingEnumeration<NameClassPair> list(Name name) throws NamingException
  • Name

使用命名服务(naming service)可以根据对象的名称找到与其相关联的对象。例如,当使用电子邮件系统时,必须提供收件人的姓名;如果要访问计算机中的文件,必须提供文件名;命名服务可以根据名称查找对象。

命名服务的主要功能是将对人友好的名称映射到对象,例如:域名系统(DNS)将机器名映射到IP地址:

www.example.com ==> 192.168.0.5

JDNI中的Name是一个表示通用名称的接口:零个或多个组件的有序序列;命名系统使用此接口定义遵循其约定的名称。

  • References

JNDI中定义了表示引用的Reference类,引用包含关于如何构造对象副本的信息。JNDI尝试将从目录查找的引用转换为它们所表示的Java对象,这样JNDI客户端就会产生一种目录中存储的是Java对象错觉。

2、Directory and LDAP

  • javax.naming.directory

此包中除了命名服务之外,还提供了访问目录服务的功能;允许应用程序检索与存储在目录中的对象关联的对象,并使用指定的属性搜索对象。

JNDI中的DirContext接口表示目录上下文,它扩展了Context接口;DirContext还可以作为命名上下文,这意味着任何目录对象也可以提供一个命名上下文。它定义了检查和更新与目录项关联属性的方法。

可以使用getAttributes方法来检索与目录项关联的属性:

Attributes getAttributes(Name name) throws NamingException

使用modifyAttributes方法添加、替换或删除属性(值):

void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException
  • javax.naming.ldap

此包中主要包含一些用于使用LDAP v3特性的类和接口,这些特性尚未被更通用的javax.naming.directory包所覆盖。

此包主要用于那些需要使用”Extended”操作、Controls或Unsolicited Notifications的应用程序。

3、Event and Service Provider

  • javax.naming.event

    此包主要包含用于在命名和目录服务中支持事件通知的类和接口。

    • Events

    NamingEvent表示由命名/目录服务生成的事件,事件包含事件类型;例如,事件类型分为影响命名空间(namespace)的事件类型(例如:”object added”)和不影响命名空间的事件类型(例如:”object changed”)。

    • Listeners

    NamingListener是监听NamingEvents的对象;每个事件类型都有一个对应的事件监听类型。例如:NamespaceChangeListener表示对命名空间更改事件感兴趣的监听器,而ObjectChangeListener表示对对象更改事件感兴趣的监听器。

  • javax.naming.spi

    不同命名/目录服务提供者的开发人员可以通过SPI提供的方式开发和连接他们的实现,从而可以让使用JNDI 的应用程序访问相应的服务。

    • 插件架构(Plug-In)

    此包允许不同的实现被动态地插入初始上下文(initial context)以及从初始上下文可以到达的上下文。

    • Java Object Support

    此包支持lookup的实现和相关方法可以返回Java对象。例如,如果从目录中查找打印机名称,则可能希望返回要对其进行操作的打印机对象。

    此包还提供了反向操作的支持,也就是说,Context.bind()和相关方法的实现可以接受Java对象,并以基础命名/目录服务可接受的格式存储对象。

    • Multiple Naming Systems

    JNDI操作允许应用程序提供跨多个命名系统的名称;在完成操作的过程中,一个服务提供者可能需要与另一个服务提供者进行交互,例如将操作继续进行。

四、样例

通过此样例来了解JNDI的实现原理。

1、程序说明

此程序提供了一个菜单服务,要求用户输入本金数额,然后根据用户的需要打印出单利、复利和单利与复利的区别。当用户不想继续使用程序时,程序将会退出。

利率固定在4.125% ,产生利息所需的年数为5年。据此计算所有的利率。

2、程序实现

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Example {

	//利率4.125%
	public static final float INTEREST_RATE = 0.04125f; 
	public static final int YEARS = 5;
	
	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("输入本金:");
		float principal = Float.parseFloat(br.readLine());
		int choice = 0;
		do{
			//重置选项
			choice = 0;
			System.out.println();
			System.out.println("------------- 菜单 ----------------");
			System.out.println("1、单利率");
			System.out.println("2、复利率");
			System.out.println("3、复利率比单利率多多少?");
			System.out.println("4、退出");
			System.out.print("请选择 : ");
			
			choice = Integer.parseInt(br.readLine());
			System.out.println();
			switch(choice){
				case 1:
					System.out.println("单利率:" + simple(principal));
					break;
				case 2:
					System.out.println("复利率:" + compound(principal));
					break;
				case 3:
					System.out.println("复利率比单利率多:" + (compound(principal) - simple(principal)));
					break;
				case 4:
					System.out.println("谢谢使用,再见!");
					break;
				default:
					System.out.println("输入无效,请重新输入!");
			}
		}while(choice != 4);
		
	}

	private static float compound(float principal) {
		return principal * (float)(Math.pow((1 + INTEREST_RATE), YEARS) - 1);
	}

	private static float simple(float principal) {
		return principal * INTEREST_RATE * YEARS;
	}
	
}

3、运行效果

输入本金:
50000

------------- 菜单 ----------------
1、单利率
2、复利率
3、复利率比单利率多多少?
4、退出
请选择 : 1

单利率:10312.5

------------- 菜单 ----------------
1、单利率
2、复利率
3、复利率比单利率多多少?
4、退出
请选择 : 2

复利率:11199.104

------------- 菜单 ----------------
1、单利率
2、复利率
3、复利率比单利率多多少?
4、退出
请选择 : 3

复利率比单利率多:886.6035

------------- 菜单 ----------------
1、单利率
2、复利率
3、复利率比单利率多多少?
4、退出
请选择 : 4

谢谢使用,再见!
参考资料: