一、JNDI 介绍
1、什么是 JNDI
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是一个Java API,允许 Java 应用程序与命名和目录服务进行交互。这些服务可以是本地的,也可以是分布式的,可以基于各种协议,如LDAP(轻量级目录访问协议)、DNS(域名系统)、NIS(网络信息服务)等。
JNDI 的主要目的是为了统一不同命名和目录服务的访问方式,使得Java应用程序能够以一种统一的方式来访问这些服务,而不必关心底层的实现细节。通过 JNDI,开发人员可以编写与特定命名服务提供者无关的代码,从而实现应用程序的可移植性和灵活性。
JNDI 可以访问的目录及服务,比如:DNS、LDAP、CORBA对象服务、RMI等等。
简单理解:在前面一节我们学到了 RMI,比如 RMI 对外提供了服务,那么 JNDI 可以通过相关 API 链接处理这些服务。
1.1、命名服务 Naming Service
命名服务提供了将名称与对象关联映射的机制。在 Java 中,这些对象通常是网络资源、Java对象、文件、服务等。命名服务允许开发人员使用简单的名称来访问这些对象,而不需要知道其底层的物理位置或其他详细信息。例如,一个Web应用程序可能需要连接到一个数据库,而不需要知道数据库的确切位置。通过命名服务,开发人员可以为数据库分配一个简单的名称,并在需要时通过该名称访问它。
1.2、目录服务 Directory Service
目录服务扩展了命名服务的概念,提供了一种更加结构化和查询友好的方式来组织和管理对象。目录服务通常被用来存储和检索关于用户、组织、网络资源等信息的数据。与命名服务类似,目录服务也使用名称来引用对象,但它们提供了更丰富的查询功能,使得可以根据各种属性进行搜索和过滤。
2、JNDI 的五个包
在 JNDI 中提供了五个作用不同的包。
- javax.naming
- javax.naming.directory
- javax.naming.ldap
- javax.naming.event
- javax.naming.spi
2.1、javax.naming
它包含了命名服务的类和接口。比如其中定义了Context接口,可以用于查找、绑定/解除绑定、重命名对象以及创建和销毁子上下文等操作。这个也是我们比较关注的一个包。
- 查找
最常用的操作是lookup()。你向lookup()提供你想要查找的对象的名称,它返回与该名称绑定的对象。
- 绑定
listBindings()返回一个名字到对象的绑定的枚举。绑定是一个元组,包含绑定对象的名称、对象的类的名称和对象本身。
- 列表
list()与listBindings()类似,只是它返回一个包含对象名称和对象类名称的名称枚举。list()对于诸如浏览器等想要发现上下文中绑定的对象的信息但又不需要所有实际对象的应用程序来说非常有用。
- 引用
在一个实际的名称服务中,有些对象可能无法直接存储在系统内,这时它们便以引用的形式进行存储。
2.1.1、InitialContext类
构造方法:
InitialContext()
构建一个初始上下文。
InitialContext(boolean lazy)
构造一个初始上下文,并选择不初始化它。
InitialContext(Hashtable<?,?> environment)
使用提供的环境构建初始上下文。
常用方法:
bind(Name name, Object obj) 将名称绑定到对象。
list(String name) 枚举在命名上下文中绑定的名称以及绑定到它们的对象的类名。
lookup(String name) 检索命名对象。
rebind(String name, Object obj) 将名称绑定到对象,覆盖任何现有绑定。
unbind(String name) 取消绑定命名对象。
2.1.2、Reference类
构造方法:
Reference(String className)
为类名为"className"的对象构造一个新的引用。
Reference(String className, RefAddr addr)
为类名为"className"的对象和地址构造一个新引用。
Reference(String className, RefAddr addr, String factory, String factoryLocation)
为类名为"className"的对象,对象工厂的类名和位置以及对象的地址构造一个新引用。
Reference(String className, String factory, String factoryLocation)
为类名为"className"的对象以及对象工厂的类名和位置构造一个新引用。
常用方法:
void add(int posn, RefAddr addr)
将地址添加到索引posn的地址列表中。
void add(RefAddr addr)
将地址添加到地址列表的末尾。
void clear()
从此引用中删除所有地址。
RefAddr get(int posn)
检索索引posn上的地址。
RefAddr get(String addrType)
检索地址类型为"addrType"的第一个地址。
Enumeration<RefAddr> getAll()
检索本参考文献中地址的列举。
String getClassName()
检索引用引用的对象的类名。
String getFactoryClassLocation()
检索此引用引用的对象的工厂位置。
String getFactoryClassName()
检索此引用引用对象的工厂的类名。
Object remove(int posn)
从地址列表中删除索引posn上的地址。
int size()
检索此引用中的地址数。
String toString()
生成此引用的字符串表示形式。
官方详细介绍:
https://docs.oracle.com/javase/tutorial/jndi/overview/naming.html
2.2、javax.naming.directory
继承了 javax.naming,提供了除命名服务外访问目录服务的功能。
官方详细介绍:
https://docs.oracle.com/javase/tutorial/jndi/overview/dir.html
2.3、javax.naming.ldap
继承了 javax.naming,提供了访问 LDAP 的能力。
官方详细介绍:
https://docs.oracle.com/javase/tutorial/jndi/overview/dir.html
2.4、javax.naming.event
包含了用于支持命名和目录服务中的事件通知的类和接口。
官方详细介绍:
https://docs.oracle.com/javase/tutorial/jndi/overview/event.html
2.5、javax.naming.spi
允许动态插入不同实现,为不同命名目录服务供应商的开发人员提供开发和实现的途径,以便应用程序通过 JNDI 可以访问相关服务。
官方详细介绍:
https://docs.oracle.com/javase/tutorial/jndi/overview/event.html
推荐拓展学习:
https://docs.oracle.com/javase/tutorial/jndi/overview/index.html
二、JNDI 操作 RMI
1、创建工程
我们在RMI基础的代码中进行一些修改。
打开rmidemo项目,在src.main.java下新建一个名为jndi的目录,并在该目录下分别新建两个名为JndiClient,JndiServer的Java Class。如下图所示:
2、JndiServer 代码
在JndiServer中键入以下代码,最终如下图所示:
package jndi;
import method.SayHelloImpl;
import javax.naming.InitialContext;
public class JndiServer {
public static void main(String[] args)throws Exception {
InitialContext initialContext = new InitialContext();
initialContext.rebind("rmi://127.0.0.1:1099/sayhello",new SayHelloImpl());
System.out.println("启动成功...");
}
}
3、JndiClient 代码
在JndiClient中键入以下代码,最终如下图所示:
package jndi;
import method.SayHello;
import javax.naming.InitialContext;
public class JndiClient {
public static void main(String[] args) throws Exception {
InitialContext initialContext = new InitialContext();
SayHello sayHello = (SayHello)initialContext.lookup("rmi://127.0.0.1:1099/sayhello");
System.out.println(sayHello.sayhello("Power7089"));
}
}
4、运行项目
①、先启动RmiServer。
②、再启动JndiServer。
③、最后启动JndiClient。
观察运行结果。
三、JNDI 操作 DNS
使用 JNDI 向 DNS 服务查询某域名 IP 地址。
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;
public class JNDIDNSLookup {
public static void main(String[] args) {
try {
// 设置JNDI属性
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
// DNS服务器地址
env.put(Context.PROVIDER_URL, "dns://114.114.114.114");
// 创建JNDI上下文对象
DirContext ctx = new InitialDirContext(env);
// 要查找的DNS名称
String dnsName = "github.com";
// 获取DNS记录的属性集合,只获取IPv4地址(A记录)
Attributes res = ctx.getAttributes(dnsName, new String[]{"A"});
// 获取IPv4地址属性
Attribute attr = res.get("A");
if (attr != null) {
// 遍历并输出所有IPv4地址
NamingEnumeration<?> ips = attr.getAll();
while (ips.hasMore()) {
System.out.println("IPv4 Address for " + dnsName + ": " + ips.next());
}
} else {
System.out.println("No IPv4 Address found for " + dnsName);
}
// 关闭上下文
ctx.close();
} catch (NamingException e) {
e.printStackTrace();
}
}
}