JNDI深入淺出


1.什么是JNDI

JNDI(The Java Naming and Directory Interface,Java命名和目錄接口)是一組在Java應用中訪問命名和目錄服務的API.命名服務將名稱和對象聯系起來,使得我們可以用名稱訪問對象。目錄服務是一種命名服務,在這種服務里,對象不但有名稱,還有屬性。

命名或目錄服務使你可以集中存儲共有信息,這一點在網絡應用中是重要的,因為這使得這樣的應用更協調、更容易管理。例如,可以將打印機設置存儲在目錄服務中,以便被與打印機有關的應用使用。

命名服務中的對象可以是DNS記錄中的名稱、應用服務器中的EJB組件(Enterprise JavaBeans Component)、LDAP(Lightweight Directory Access Protocol)中的用戶Profile.

目錄服務是命名服務的自然擴展。兩者之間的關鍵差別是目錄服務中對象可以有屬性(例如,用戶有email地址),而命名服務中對象沒有屬性。因此,在目錄服務中,你可以根據屬性搜索對象。目錄服務允許你訪問文件系統中的文件,定位遠程RMI注冊的對象,訪問象LDAP這樣的目錄服務,定位網絡上的EJB組件。

2.JNDI的優勢

解耦合(Decoupling):通過注冊、查找JNDI服務,可以直接使用服務,而無需關心服務提供者,這樣程序不至於與訪問的資源耦合!(高內聚:high cohesion)

包含了大量的命名和目錄服務,使用通用接口來訪問不同種類的服務;可以同時連接到多個命名或目錄服務上;建立起邏輯關聯,允許把名稱同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的命名服務。

3.JNDI的基本運行原理

  a.注冊JNDI提供者(register)

在使用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

  b.連接服務(init)

注冊之后,就可以實施服務連接了。對於名字服務由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);

c.查找服務(lookup)

不論名字服務還是目錄服務,都是使用lookup來查找對象的。除了可以使用String作為參數之外,lookup還可使用Name接口作為參數。
Greeter greeter = (Greeter)ctx.lookup("SayHello");
如果想要獲得上下文中所有的對象名字,就使用list返回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()); 
}

d.對象綁定(binding)

- 使用bind添加綁定
Fruit fruit = new Fruit("orange");
ctx.bind("favorite", fruit);

- 使用rebind修改綁定
Fruit fruit = new Fruit("lemon");
ctx.rebind("favorite", fruit);

- 使用unbind去除綁定。
ctx.unbind("favorite");

e.對象改名(rename)

使用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);

f.查找及過濾(Search and Filter)

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

g:修改屬性

使用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);

h.創建上下文

使用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);

i.刪除上下文

使用destroySubcontext方法完成。
initial_ctx.destroySubcontext(dn);

3.JNDI與JDBC

JNDI提供了一種統一的方式,可以用在網絡上查找和訪問服務。通過指定一個資源名稱,該名稱對應於數據庫或命名服務中的一個紀錄,同時返回數據庫連接建立所必須的信息。

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

4.JNDI與JMS

消息通信是軟件組件或應用程序用來通信的一種方法。JMS就是一種允許應用程序創建、發送、接收、和讀取消息的JAVA技術。

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

5.JNDI與Spring集成(integrate)

查找connection

<bean id="tuxedoConnFactory" class="org.springframework.jndi.JndiObjectFactoryBean">   
      <property name="jndiName"> 
            <value>tuxedo/services/TuxedoConnection</value>
      </property> 
      <property name="resourceRef"> 
          <value>false</value>
     </property> 
     <property name="jndiEnvironment"> 
       <props> 
            <!-- The value of Context.PROVIDER_URL --> 
            <prop key="java.naming.provider.url">t3://localhost:7001</prop> 
            <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop> 
        </props> 
      </property> 
</bean>

查找datasource

(1)配置可以訪問到同一應用服務器的jndi數據源 

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> 

<property name="jndiName"> 

<value>jdbc/cqccms</value> 

</property> 

</bean>
(2)配置能訪問遠程jndi數據源 

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> 

<property name="jndiName"> 

<value>jdbc/cqccms</value> 

</property> 

<property name="jndiEnvironment"> 

<props> 

<prop key="java.naming.factory.initial"> 

weblogic.jndi.WLInitialContextFactory 

</prop> 

<prop key="java.naming.provider.url">t3://172.16.101.42:7001</prop> 

<prop key="java.naming.security.principal">weblogic</prop> 

<prop key="java.naming.security.credentials">weblogic</prop> 

</props> 

</property> 

</bean>

遠程數據源的事務配置

<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate" singleton="true" 

lazy-init="default" autowire="default" dependency-check="default"> 

<property name="environment"> 

<props> 

<prop key="java.naming.factory.initial"> 

weblogic.jndi.WLInitialContextFactory 

</prop> 

<prop key="java.naming.provider.url">t3://172.16.101.42:7001</prop> 

<prop key="java.naming.security.principal">weblogic</prop> 

<prop key="java.naming.security.credentials">weblogic</prop> 

</props> 

</property> 

</bean> 

然后在配置一下transactionManager,如下 

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" singleton="true" 

lazy-init="default" autowire="default" dependency-check="default"> 

<property name="jndiTemplate"> 

<ref local="jndiTemplate" /> 

</property> 

<property name="userTransactionName"> 

<value>weblogic/transaction/UserTransaction</value> 

</property> 

</bean>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM