JNDI(Java Naming and Directory Interface)- Java命名和目录接口,是一组在Java应用中访问命名和目录服务的API。为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。
命名服务将名称和对象联系起来,可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称还有属性。
一、命名服务与目录服务
命名服务:作为一种基础设施出现在众多的计算机系统,其意义在于提供根据名称关联对象和通过对象匹配相应的命名服务。命名服务允许你通过名称找到一个与之对应的对象。例如在Internet上的域名服务(domain namingservice,DNS)就是提供将域名映射到IP地址的命名服务,在打开网站时一般都是在浏览器中输入名字,通过DNS找到相应的IP地址,然后打开。
目录服务:是一种特殊类型的数据库,与SQLServer、Access、Oracle等关系数据库管理系统相反,构造目录服务的目的是为了处理基于行为的事务,并且使用一种关系信息模型。
目录服务将命名服务的概念进一步引申为提供具有层次结构的信息库,这一信息库除了包含一对一的关系外,还有信息的层次结构。对目录服务而言,这种层次结构通常用于优化搜索操作,并且也可以按实际情况进行分布或者跨网络复制。
目录服务是命名服务,命名服务不需要是目录服务。
命名和目录服务把标识符和资源关联起来。
二、JNDI基础知识
1.什么是JNDI
JNDI用于定位查找服务对象,内部是以一种树状的目录结构,如下图:
我们只需将服务端的相关参数传给JNDI API就可以了,具体调用过程由SPI来完成。
2.使用JNDI有哪些好处?
JNDI将一些关键信息放到内存中,可以提高访问效率;通过jndi可以达到解耦的目的,是连接不同对象的有利纽带。
3.什么时候使用JNDI?
JNDI是一种查找服务,用于查找:
web应用环境变量、EJBs和他们的环境变量、通过DataSource的数据库连接池、JMS目标和连接工厂、其他服务
4.使用JNDI的注意事项
不要将JNDI当做数据库使用、JNDI对象存储在内存中、访问JNDI对象与网络性能有关
5.JNDI的常用方法有哪些?
使用bind、rebind方法进行绑定使用unbind方法解除绑定
使用lookup方法查找
使用InitialContext方法初始化,可以包含参数,不包含参数的时候使用默认值
远程绑定对象必须是序列化的,访问命名服务是,对象采用的是复制机制,也就是值的复制
如果使用本地对象,使用的是变量的引用
三、JNDI架构
JNDI架构与JDBI架构非常类似。JNDI架构提供了一组标准命名系统的API。在应用程序中,我们实际上只是用以上几个包的种类。具体调用类以及通信过程对用户来说是透明的。JNDI API提供了访问不同JNDI服务的一个标准的统一实现,其具体实现可由不同的Service Provider来完成。
中间层为命名管理层。其功能应该由JNDI SPI来完成。
最下层为JNDI SPI API及其具体实现。
四、JNDI程序包
javax.naming:命名操作
javax.naming.direcotry:目录操作
javax.naming.event:在命名目录服务器中请求事件通知
javax.naming.ldap:提供LDAP支持
javax.naming.spi:允许动态插入不同实现
利用JNDI的命名与服务功能来满足企业级API对命名与服务的访问,诸如EJB、JMS、JDBC 2.0以及IIOP上的RMI通过JNDI来使用CORBA的命名服务
五、JNDI环境变量
环境变量 | 相应的常量 | 说明 |
---|---|---|
java.naming.factory.initial | Context.INITIAL_CONTEXT_FACTORY | Context Factory类名,由服务提供商给出。 |
java.naming.provider.url | Context.PROVIDE_URL | 初始化地址。 |
java.naming.security.principal | Context.SECURITY_PRINCIPAL | 服务使用者信息。 |
java.naming.security.credentials | Context.SECURITY_CREDENTIAL | 口令。 |
六、使用示例
1.连接服务
从服务器端连接到JNDI:默认当前,返回一个context
//创建一个InitialContext
context ctx= new InitialContext();
从任何一个地反连接到jndi:需要先设置环境
//创建Environment对象
weblogic.jndi.Enviroment env = new weblogic.jndi.Enviroment();
//填写Environment
//weblogic server的位置
env.setProviderurl("t3://127.0.0.1:7011");
env.setSecurityPrincipal("System");
//安全信息
env.setSecurityCredenttials("PassWord");
//使用Environment对象创建InitialContext
context ctx =env.getInitialContext();
使用hash类链接jndi
Hashtble env = new Hashtble();
Env.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
Env.put(Context.PROVIDER_URL,"T3://LOCALHOST:7001");
Evn.put(Context.SECURITY_PRINCIPAL,"system");
Env.put(Cotext.SECURIRY_CRRDENTIALS,"PassWord");
//此处有三种构造函数
Context ctx = new InitialContext(env);
使用Hardcoded名和Properties类
Properties env = new Properties();
env.setProperty("java.naming.factory.initial","weblogic.jndi.WLInitialContextFactory");
env.setProperty("java.naming.provider.url","t3:192.2.256.102:7001");
env.setproperty("java.naming.securiry.principal","system");
env.setProperty("java.naming.security.credentials","PaSsWoRd");
Context ctx = new InitialContext(env);
2.使用默认值
jndi.properties文件为所有的Initial Contexts设置默认属性,且文件的的搜索次序为:CLASSPATH其次是$JAVA_HOME/lib/
Java.naming.factory.initial = weblogic.jndi.WLInitialContextFactory;
Java.naming.provider.url=t3://localhost:7001;
Java.naming.security.principal =system;
Java.naming.security.credentials =password;
//没有参数,使用这些默认值
Context ctx = new InitialContext();
3.创建一个用于绑定对象的新Subcontext
//创建Environment对象
Context ctx =env.getInitialContext();//首先必须获得存在的Subcontext或InitialContext
//填写Environment
Context subcontext =ctx.createSubcontext("newSbucontext");//在当前的Context下创建新的Subcontext
subcontext.rebind("boundObject",object);//绑定一个对象
//当结束时经常关闭访问的资源
subcontext.close();
ctx.close();
4.查找对象
从JNDI查找对象,反序列化后,使用事务(多分支的事务),Lookup()从JNDI树获得对象。
import javax.naming.*;
try {
Context ic = new Context();
Object obj;
// 查找对象
obj = ic.lookup("javax.transaction.UserTransaction");
// 反序列化和事务
UserTransaction ut = (UserTransaction)obj;
ut.begin();
// 分支事务
ic.close();
} catch(NamingException e){
//
}
5.远程绑定对象
在客户端使用的时候需要反序列化,传送到服务端的时候是序列化的。
public static Context getInitialContext() throws NamingException{
Environment env = new Environment();
env.setProviderUrl("t3://localhost:7001");
env.setSecurityPrincipal("system");
env.setSecurityCredendtials("weblogic");
Context context = env.getInitialContext();
return context;
}
//获得initial contex
Context ctx = getInitialContext();
//创建名为Bank的对象
Bank myBank = new Bank();
//把对象绑定到JNDI树
ctx.rebind("theBank",myBank);
ctx.close();
6.un-bind
public static Context getInitialContext() throws NamingException{
Environment env = new Environment();
env.setProviderUrl("t3://localhost:7001");
env.setSecurityPrincipal("system");
env.setSecurityCredendtials("weblogic");
Context context = env.getInitialContext();
return context;
}
//获得initial contex
Context ctx = getInitialContext();
//创建名为Bank的对象
Bank myBank = new Bank();
//解除绑定
ctx.unbind("theBank");
ctx.close();
7.对象改名
ctx.rename("report.txt", "old_report.txt");
8.获取属性
String dn = "uid=me, dc=mycompany, dc=com, ou=customer, o=ExampleApp";
Context user = (Context)ctx.lookup(dn);
//获得所有属性
Attributes attrs = user.getAttributes("");
Attribute test= attrs .get("test");
Object testValue= test.get();
9.修改属性
public void updateAddress(String dn,String address, String country, String phone) {
BasicAttributes mod_attrs = new BasicAttributes();
if(address != null)
mod_attrs.put("address", address);
if(country != null)
mod_attrs.put("c", country);
if(phone != null)
mod_attrs.put("phonenumber", phone);
if(mod_attrs.size() != 0)
initial_ctx.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, mod_attrs);
}
10.使用jndi可能的异常
AuthenticationException
CommunicationException
InvalidNameException
NameNotFoundException
NoInitialcontextException
七、总结
JNDI的思想是提供一个通用的接口。底层的名字服务时限可以是各种类型,JNDI通过SPI时限代码屏蔽了各种名字和目录服务实现的区别。
JNDI可以解决数据共享,分布式应用,紧耦合的问题,所以可以应用在一些比较大的软件开发中,分布式系统中或者是比较重视后期维护和升级的项目中。