Java中的命名与目录接口JNDI基本操作方法概览_java

对jndi总体的理解:

jndi(java naming and directory Interface)它提供了一套使用命名和目录服务的接口。用户可以通过它来使用命名和目录服务。就像jdbc一样。jndi包括命名服务和目录服务两部分,其中目录服务包含目录对象directory object,它包含若干属性对象。提供了对属性的很多操作。
命名和目录服务:

命名和目录服务我们一直在使用,如操作系统的文件系统,它给我们提供对文件的操作,查询,添加删除等功能。DNS服务将url同ip地址绑定在了一起。命名和目录系统的最主要的功能是将name和对象绑定。在它的基础之上还提供更多的功能如lookup,search.而且存储的对象是有一定层次结构的。使用这样的服务我们可以对对象更加有效的管理和操作。
命名服务将一个名称映射到一个对象。RMI Registry和CORBA Naming Service都是命名服务。
目录服务也存放对象,但目录服务识别这些对象的相关属性。可以用项目属性来搜索目录。
在20世纪90年代早期,轻量级的目录访问协议(LightWeightDiretoryAccessProtocol,LDAP)被作为一种标准的目录协议被开发出来,JNDI能够访问LDAP。
j2se为jndi提供了5个扩展包:

  • javax.naming;为访问命名服务提供的api
  • javax.naming.directory;为访问目录服务提供了基本的接口
  • javax.naming.event;支持命名和目录服务中的事件通知
  • javax.naming.ldap; 支持ldap的包,
  • javax.naming.spi;提供了不同命名和目录服务可以挂接他们的实现的方法。

context: context是一套name-to-object的绑定(bindings),可以理解为层次或目录,它还可已包括下一层subContext。在使用命名和目录服务时获得initial context是对整个名字空间操作的入口。在目录服务中是DirContext.
JNDI(Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。
JNDI可访问的现有的目录及服务有:
DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol 轻型目录访问协议)、 CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v2、NIS。
JNDI优点:
包含了大量的命名和目录服务,使用通用接口来访问不同种类的服务;可以同时连接到多个命名或目录服务上;建立起逻辑关联,允许把名称同Java对象或资源关联起来,而不必指导对象或资源的物理ID。

JNDI程序包:

  • javax.naming:命名操作;
  • javax.naming.directory:目录操作;
  • javax.naming.event:在命名目录服务器中请求事件通知;
  • javax.naming.ldap:提供LDAP支持;
  • javax.naming.spi:允许动态插入不同实现。
  • 利用JNDI的命名与服务功能来满足企业级APIs对命名与服务的访问,诸如EJBs、JMS、JDBC 2.0以及IIOP上的RMI通过JNDI来使用CORBA的命名服务。

JNDI与JDBC:
JNDI提供了一种统一的方式,可以用在网络上查找和访问服务。通过指定一个资源名称,该名称对应于数据库或命名服务中的一个纪录,同时返回数据库连接建立所必须的信息。
代码示例:

try{
Context cntxt = new InitialContext();
DataSource ds = (DataSource) cntxt.lookup("jdbc/dpt");
}
catch(NamingException ne){
...
}

JNDI与JMS:
消息通信是软件组件或应用程序用来通信的一种方法。JMS就是一种允许应用程序创建、发送、接收、和读取消息的JAVA技术。
代码示例:

try{
Properties env = new Properties();
InitialContext inictxt = new InitialContext(env);
TopicConnectionFactory connFactory = (TopicConnectionFactory) inictxt.lookup("TTopicConnectionFactory");
...
}
catch(NamingException ne){
...
}

访问特定目录:举个例子,人是个对象,他有好几个属性,诸如这个人的姓名、电话号码、电子邮件地址、邮政编码等属性。通过getAttributes()方法

Attribute attr = directory.getAttributes(personName).get("email");
String email = (String)attr.get();

通过使用JNDI让客户使用对象的名称或属性来查找对象:

foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls);

通过使用JNDI来查找诸如打印机、数据库这样的对象,查找打印机的例子:

Printer printer = (Printer)namespace.lookup(printerName);
printer.print(document);

浏览命名空间:

NamingEnumeration list = namespace.list("o=Widget, c=US");
while (list.hasMore()) {
NameClassPair entry = (NameClassPair)list.next();
display(entry.getName(), entry.getClassName());
}
  • 常用的JNDI操作:
  • void bind(String sName,Object object);――绑定:把名称同对象关联的过程
  • void rebind(String sName,Object object);――重新绑定:用来把对象同一个已经存在的名称重新绑定
  • void unbind(String sName);――释放:用来把对象从目录中释放出来
  • void lookup(String sName,Object object);――查找:返回目录总的一个对象
  • void rename(String sOldName,String sNewName);――重命名:用来修改对象名称绑定的名称
  • NamingEnumeration listBinding(String sName);――清单:返回绑定在特定上下文中对象的清单列表
  • NamingEnumeration list(String sName);

代码示例:重新得到了名称、类名和绑定对象。

NamingEnumeration namEnumList = ctxt.listBinding("cntxtName");
...
while ( namEnumList.hasMore() ) {
Binding bnd = (Binding) namEnumList.next();
String sObjName = bnd.getName();
String sClassName = bnd.getClassName();
SomeObject objLocal = (SomeObject) bnd.getObject();
}

了解名字服务和目录服务的相关概念,有助于更好的使用JNDI。 Naming service       名字服务定义了如何将名字与对象关联,并通过名字如何找到对象的方法。典型的例子如:DNS将域名与IP关联,文件系统将文件名与文件相关联。在名字服务中,主要的概念:
-          名字(Names),在名字系统中实际对象的代号,如文件名,域名等,它会被用来查找关联的对象。不同的系统中会有不同的命名规范,如文件系统采用“\”来表示层级,而DNS则使用“.”。
-          绑定(Bindings),名字和实际对象的关联。
-          引用和地址(References and Addresses),当对象不能直接被存储在名字系统时,就必须使用引用,通过引用找到实际的对象。在系统中,保存的引用的内容被称为地址。引用还有另一个用处:在名字系统中,缺少象关系数据库中外键的概念。通过使用引用,可以作为外键的一个取代办法。
-          上下文(Context),它是一个名字-对象集合,提供了与名字系统交互的主要操作,如查找、绑定、去绑定。子上下文(subcontext)与它的关系类似文件系统中目录和子目录的关系,子上下文被包含在一个上下文中,通过父上下文中的一个名字与子上下文关联。
-          名字系统和名字空间(Naming Systems and Namespaces),名字系统是相同类型的上下文的集合,它提供名字服务;名字空间,是名字系统中的名字集合,如文件系统的文件名和目录。
Directory service       目录服务是名字服务的扩展,它除了关联名字和对象,还允许对象包含属性。目录系统通常以层次结构组织数据。在目录服务中的主要概念:
-          属性(Attributes),它属于目录对象,它是(名字,值)对,属性可以有多个值。
-          目录和目录服务(Directories and Directory Services),目录是目录对象的集合;目录服务则提供与目录相关的服务,创建、删除和修改存放在目录中的对象的属性。
-          查找和查找过滤器(Searches and Search Filters),获取目录对象的操作就是查找;过滤器是类似查找条件的对象。
基本使用
 
²        注册JNDI提供者
在使用JNDI之前,需要先获取JNDI的提供者,并在系统注册它。与JNDI相关的系统属性在javax.naming.Context中定义,常用的属性:
-          java.naming.factory.initial,服务提供者用来创建InitialContext的类名。
-          java.naming.provider.url,用来配置InitialContext的初始url
-          java.naming.factory.object,用来创建name-to-object映射的类,用于NameClassPair和References。
-          java.naming.factory.state,用来创建jndi state的类
对于目录服务,由于一般需要安全设置,还通常使用:
-          java.naming.security.authentication,安全类型,三个值:none,simple或strong。
-          java.naming.security.principal,认证信息。
-          java.naming.security.credentials,证书信息。
-          java.naming.security.protocol,安全协议名。
使用System.setProperty注册,如果程序不显示说明,那么java会在classpath内查找jdni.properties文件来完成注册。jdni.properties例子:
java.naming.factory.initial=com.codeline.db.MockInitialContextFactory
 
连接服务
注册之后,就可以实施服务连接了。对于名字服务由InitialContext开始,目录服务则使用InitialDirContext。它们分别实现了Context和DirContext,这两个接口分别对应名字服务和目录服务的接口,也是JNDI中最重要的两个接口。
连接名字服务:                

System.setProperty(Context.INITIAL_CONTEXT_FACTORY,"
com.sun.jndi.fscontext.FSContextFactory");
InitialContext ctx = new InitialContext();

连接目录服务:

 Hashtable env = new Hashtable();
 env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
 env.put(Context.PROVIDER_URL, "ldap://myserver.com/");
 env.put(Context.SECURITY_AUTHENTICATION, "simple");
 //登录ldap server需要的用户名
 env.put(Context.SECURITY_PRINCIPAL, "ldapuser");
 //登录ldap server需要的密码
 env.put(Context.SECURITY_CREDENTIALS, "mypassword");
InitialDirContext ctx = new InitialDirContext(env);

 
多服务提供者:如果应用包含多个服务提供者,在连接时略有不同。以名字服务为例

Hashtable env = new Hashtable();
 env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.rmi.registry.RegistryContextFactory");
 env.put(Context.PROVIDER_URL, "rmi://myserver.com:1099");
 //使用不同的构造函数
InitialContext ctx = new InitialContext(env);

 
查找对象
不论名字服务还是目录服务,都是使用lookup来查找对象的。除了可以使用String作为参数之外,lookup还可使用Name接口作为参数。

Greeter greeter = (Greeter)ctx.lookup("SayHello");

如果想要获得上下文中所有的对象名字,就使用lis返回NameClassPair列表。NameClassPair包含对象名字和对象类名。如果想要获得实际的对象实例列表,就使用listBindings,它返回Binding列表。Binding是NameClassPair的子类,它包含对象的实例。
-  list

NamingEnumeration list = ctx.list("awt");
while (list.hasMore()) {
 NameClassPair nc = (NameClassPair)list.next();
 System.out.println(nc);
}

-  listBindings

NamingEnumeration bindings = ctx.listBindings("awt");
while (bindings.hasMore()) {
 Binding bd = (Binding)bindings.next();
 System.out.println(bd.getName() + ": " + bd.getObject());
}

 
对象绑定
- 使用bind添加绑定

Fruit fruit = new Fruit("orange");
ctx.bind("favorite", fruit);

- 使用rebind修改绑定

Fruit fruit = new Fruit("lemon");
ctx.rebind("favorite", fruit);

- 使用unbind去除绑定。

ctx.unbind("favorite");

 
对象改名
使用rename可以给一个在上下文中的对象改名

ctx.rename("report.txt", "old_report.txt");

- 获取属性
属性相关的接口是Attribute和Attributes,它们都在javax.naming.directory包内。通过DirContext的getAttributes方法就可以获得对象的属性集合,然后使用Attributes的get方法获得对应的属性,最后通过Attribute的get方法就可以获得属性值。

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();

上例中获得的是user的所有属性,在实际使用过程中,考虑网络带宽的影响,可以设置获取要获取的属性列表:

String reqd_attrs = new String[] { "surname", "initials","title", "rfc822mailalias"};
Attributes attrs = user.getAttributes("", reqd_attrs);

 
查找和过滤
使用search方法完成。

 public DirContext[] findUser(String initials,String surname,String country,String phone) {
  //构造条件
   BasicAttributes search_attrs = new BasicAttributes();
  search_attrs.put("initials", initials);
  search_attrs.put("sn", surname);
  search_attrs.put("c", country);
  if(phone != null)
   search_attrs.put("phonenumber", phone);
  NamingEnumeration results = initial_ctx.search("ou=Customer,o=ExampleApp", search_attrs);
  LinkedList found = new LinkedList();
  while(results.hasMore()) {
   SearchResults sr = (SearchResults)results.next();
   String name = sr.getName();
   Object ctx = sr.getObject();
   if((ctx == null) || !(ctx instanceof DirContext))
    found.add(initial_ctx.lookup(name));
   else
    found.add(ctx);
  }
  DirContext[] ret_val = new DirContext[found.size()];
  found.toArray(ret_val);
  return ret_val;
 }

 
DirContext接口主要过滤方式:

1.使用过滤字符串 

String reqd_attrs = new String[] { "cn", "uid","rfc822mailalias" };
NamingEnumeration results = initial_ctx.search("ou=Customer, o=ExampleApp",search_attrs,reqd_attrs);

 
2.使用SearchControls,获得更多的控制

SearchControls ctrls = new SearchControls();
ctrls.setCountLimit(20);
ctrls.setTimeLimit(5000);
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration results = initial_ctx.search("cat=books,ou=Products,
o=ExampleApp","title=*Java*",ctrls);

修改属性
使用DirContext和InitialDirContext的modifyAttributes方法完成。所谓的修改过程,实际就是先构造要修改的属性列表,然后使用上述方法提交。对于属性包含多个值时,需要把属性的不修改的值也要包含,否则服务器会认为那些值不再需要而删除它们。

 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);
}

使用ModificationItem,也可一次进行多个不同的修改操作:

ModificationItem[] mod_items = new ModificationItems[2];
Attribute email = new BasicAttribute("rfc822mailalias", new_email);
ModificationItem email_mod = new ModificationItem(DirContext.ADD_ATTRIBUTE, email);
Attribute addr = new BasicAttribute("address", address);
ModificationItem addr_mod = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, addr);
mod_items[0] = email_mod;
mod_items[1] = addr_mod;
initial_ctx.modifyAttributes(dn, mod_items);

创建上下文
使用createSubcontext方法完成。

 BasicAttributes attrs = new BasicAttributes();
attrs.put("initials", initials);
 attrs.put("sn", surname);
 attrs.put("rfc822mailalias", email);
 if(address != null)
  attrs.put("address", address);
 if(country != null)
  attrs.put("c", country);
 if(phone != null)
 attrs.put("phonenumber", phone);
initial_ctx.createSubcontext(dn, attrs);

 
删除上下文
使用destroySubcontext方法完成。

initial_ctx.destroySubcontext(dn);

实例
以下再举一个实例。

在tomcat的conf/server.xml中配置:

<Context path="/jndi"> 

 <Resource name="bean/MyBeanFactory" auth="Container"
   type="com.huawei.jndi.bean.MyBean"
   factory="org.apache.naming.factory.BeanFactory"
   bar="23"/> 

</Context> 

上面就在tomcat中声明了一个组件,接下来在代码中可以获取这个组件:

try
{
 Context initContext = new InitialContext();
 Context envCtx = (Context) initContext.lookup("java:comp/env");
 MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory");
 System.out.println(bean.getBar());
}
catch (Exception e)
{
 e.printStackTrace();
} 

总结:在tomcat中配置jndi组件,然后在代码中获取已配好的组件。

各WEB容器的JNDI实现类是不同的,比如在JBOSS中,JNDI提供类是org.jnp.interfaces.NamingContextFactory,与tomcat是不同的。

这样看来,JNDI的作用和spring的依赖注入倒是差不多。但是通过JNDI,可以实现跨应用,甚至跨域获取组件。在服务器A上配置的组件,在另一台服务器B上,可以通过JNDI获取到。

spring也提供了对jndi的封装,使用起来更加方便,以下是一个例子。

<!-- JNDI模板 -->
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
 <property name="environment">
  <props>
   <prop key="java.naming.factory.initial">org.jnp.interfaces.NamingContextFactory</prop>
   <prop key="java.naming.provider.url">10.137.96.212:18199</prop>
   <prop key="java.naming.factory.url.pkgs">org.jnp.interfaces:org.jboss.naming</prop>
  </props>
 </property>
</bean> 

<!-- 创建连接工厂 -->
<bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
 <property name="jndiTemplate" ref="jndiTemplate" />
 <property name="jndiName" value="TopicConnectionFactory" />
</bean> 

先声明JndiTemplate,配置好目标地址、JNDI服务提供类。然后通过JndiObjectFactoryBean,就可以很方便地获取JNDI组件,并进行类型转换。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
jndi
jndi命名方法、接口命名规范、java接口命名规范、linux目录重命名、java 接口命名,以便于您获取更多的相关知识。

时间: 2024-09-05 13:43:53

Java中的命名与目录接口JNDI基本操作方法概览_java的相关文章

Java命名与目录接口——JNDI

JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE特别是EJB的知识.JNDI到底起什么作用?   没有JNDI的做法 程序员开发时,知道要开发访问MySQL数据库的应用,于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到数据库. 就像以下代码这样: Connection co

Java命名和目录接口——JNDI

JNDI即Java命名和目录接口(JavaNaming and Directory Interface),它属于J2EE规范范畴,是J2EE的核心技术之一,提供了一组接口.类和关于命名空间的概念.JDNI是provider-based技术,它暴露一个API和一个服务供应接口(SPI).它将名称和对象联系起来,使我们可以用名称访问对象.我们可以把JNDI简单地看成是里面封装了一个name到实体对象的映射,通过字符串可以方便得到想要的对象资源,例如JDBC.JMail.JMS.EJB等.这意味着任何

详解Java中的File文件类以及FileDescriptor文件描述类_java

File File 是"文件"和"目录路径名"的抽象表示形式. File 直接继承于Object,实现了Serializable接口和Comparable接口.实现Serializable接口,意味着File对象支持序列化操作.而实现Comparable接口,意味着File对象之间可以比较大小:File能直接被存储在有序集合(如TreeSet.TreeMap中).1. 新建目录的常用方法方法1:根据相对路径新建目录. 示例代码如下(在当前路径下新建目录"d

Java中JDBC事务与JTA分布式事务总结与区别_java

Java事务的类型有三种:JDBC事务.JTA(Java Transaction API)事务.容器事务.常见的容器事务如Spring事务,容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现.所以本文暂不讨论容器事务.本文主要介绍J2EE开发中两个比较基本的事务:JDBC事务和JTA事务. JDBC事务 JDBC的一切行为包括事务是基于一个Connection的,在JDBC中是通过Connection对象进行事务管理.在JDBC中,

Java中String、StringBuffer、StringBuilder的区别详解_java

 java中String.StringBuffer.StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题.现在总结一下,看看他们的不同与相同. 1.可变与不可变 String类中使用字符数组保存字符串,如下就是,因为有"final"修饰符,所以可以知道string对象是不可变的. private final char value[]; StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在

Java中数组的创建与传参方法(学习小结)_java

(一)数组的创建 数组的创建包括两部分:数组的申明与分配内存空间. int score[]=null; //申明一维数组 score=new int[3]; //分配长度为3的空间 数组的申明还有另外一种方式: int[] score=null; //把中括号写在数组名前面 通常,在写代码时,为了方便,我们将两行合并为一行: int score[]=new int score[3]; //将数组申明与分配内存写在一行 (二)传递参数 由于初学java,这里只讨论值传递,不考虑地址传递.主要有3点

详解Java中的线程让步yield()与线程休眠sleep()方法_java

线程让步: yield()yield()的作用是让步.它能让当前线程由"运行状态"进入到"就绪状态",从而让其它具有相同优先级的等待线程获取执行权:但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权:也有可能是当前线程又进入到"运行状态"继续运行!示例: class ThreadA extends Thread{ public ThreadA(String name){ super(name); } pu

实例分析Java中public static void main(String args[])是什么意思_java

本文实例讲述了Java中public static void main(String args[])的来龙去脉.分享给大家供大家参考,具体如下: public static void main(String[] args) 这绝对不是凭空想出来的,也不是没有道理的死规定,而是java程序执行的需要. jvm在试图运行一个类之前,先检查该类是否包含一个特殊方法.这个方法必须是公有的,以便在任何位置都能访问得到.这个方法必须是static的,因为这个方法不能依赖任何该类的实例即可运行,而非stati

浅谈Java中父类与子类的加载顺序详解_java

复制代码 代码如下: class Parent {    // 静态变量    public static String p_StaticField = "父类--静态变量";    // 变量(其实这用对象更好能体同这一点,如专门写一个类的实例)     //如果这个变量放在初始化块的后面,是会报错的,因为你根本没有被初始化    public String p_Field = "父类--变量";    // 静态初始化块    static {        S