Java中的命名與目錄接口JNDI基本操作方法概覽
對(duì)jndi總體的理解:
jndi(java naming and directory Interface)它提供了一套使用命名和目錄服務(wù)的接口。用戶可以通過它來使用命名和目錄服務(wù)。就像jdbc一樣。jndi包括命名服務(wù)和目錄服務(wù)兩部分,其中目錄服務(wù)包含目錄對(duì)象directory object,它包含若干屬性對(duì)象。提供了對(duì)屬性的很多操作。
命名和目錄服務(wù):
命名和目錄服務(wù)我們一直在使用,如操作系統(tǒng)的文件系統(tǒng),它給我們提供對(duì)文件的操作,查詢,添加刪除等功能。DNS服務(wù)將url同ip地址綁定在了一起。命名和目錄系統(tǒng)的最主要的功能是將name和對(duì)象綁定。在它的基礎(chǔ)之上還提供更多的功能如lookup,search.而且存儲(chǔ)的對(duì)象是有一定層次結(jié)構(gòu)的。使用這樣的服務(wù)我們可以對(duì)對(duì)象更加有效的管理和操作。
命名服務(wù)將一個(gè)名稱映射到一個(gè)對(duì)象。RMI Registry和CORBA Naming Service都是命名服務(wù)。
目錄服務(wù)也存放對(duì)象,但目錄服務(wù)識(shí)別這些對(duì)象的相關(guān)屬性??梢杂庙?xiàng)目屬性來搜索目錄。
在20世紀(jì)90年代早期,輕量級(jí)的目錄訪問協(xié)議(LightWeightDiretoryAccessProtocol,LDAP)被作為一種標(biāo)準(zhǔn)的目錄協(xié)議被開發(fā)出來,JNDI能夠訪問LDAP。
j2se為jndi提供了5個(gè)擴(kuò)展包:
- javax.naming;為訪問命名服務(wù)提供的api
- javax.naming.directory;為訪問目錄服務(wù)提供了基本的接口
- javax.naming.event;支持命名和目錄服務(wù)中的事件通知
- javax.naming.ldap; 支持ldap的包,
- javax.naming.spi;提供了不同命名和目錄服務(wù)可以掛接他們的實(shí)現(xiàn)的方法。
context: context是一套name-to-object的綁定(bindings),可以理解為層次或目錄,它還可已包括下一層subContext。在使用命名和目錄服務(wù)時(shí)獲得initial context是對(duì)整個(gè)名字空間操作的入口。在目錄服務(wù)中是DirContext.
JNDI(Java Naming and Directory Interface)是一個(gè)應(yīng)用程序設(shè)計(jì)的API,為開發(fā)人員提供了查找和訪問各種命名和目錄服務(wù)的通用、統(tǒng)一的接口,類似JDBC都是構(gòu)建在抽象層上。
JNDI可訪問的現(xiàn)有的目錄及服務(wù)有:
DNS、XNam 、Novell目錄服務(wù)、LDAP(Lightweight Directory Access Protocol 輕型目錄訪問協(xié)議)、 CORBA對(duì)象服務(wù)、文件系統(tǒng)、Windows XP/2000/NT/Me/9x的注冊(cè)表、RMI、DSML v1&v2、NIS。
JNDI優(yōu)點(diǎn):
包含了大量的命名和目錄服務(wù),使用通用接口來訪問不同種類的服務(wù);可以同時(shí)連接到多個(gè)命名或目錄服務(wù)上;建立起邏輯關(guān)聯(lián),允許把名稱同Java對(duì)象或資源關(guān)聯(lián)起來,而不必指導(dǎo)對(duì)象或資源的物理ID。
JNDI程序包:
- javax.naming:命名操作;
- javax.naming.directory:目錄操作;
- javax.naming.event:在命名目錄服務(wù)器中請(qǐng)求事件通知;
- javax.naming.ldap:提供LDAP支持;
- javax.naming.spi:允許動(dòng)態(tài)插入不同實(shí)現(xiàn)。
- 利用JNDI的命名與服務(wù)功能來滿足企業(yè)級(jí)APIs對(duì)命名與服務(wù)的訪問,諸如EJBs、JMS、JDBC 2.0以及IIOP上的RMI通過JNDI來使用CORBA的命名服務(wù)。
JNDI與JDBC:
JNDI提供了一種統(tǒng)一的方式,可以用在網(wǎng)絡(luò)上查找和訪問服務(wù)。通過指定一個(gè)資源名稱,該名稱對(duì)應(yīng)于數(shù)據(jù)庫(kù)或命名服務(wù)中的一個(gè)紀(jì)錄,同時(shí)返回?cái)?shù)據(jù)庫(kù)連接建立所必須的信息。
代碼示例:
try{ Context cntxt = new InitialContext(); DataSource ds = (DataSource) cntxt.lookup("jdbc/dpt"); } catch(NamingException ne){ ... }
JNDI與JMS:
消息通信是軟件組件或應(yīng)用程序用來通信的一種方法。JMS就是一種允許應(yīng)用程序創(chuàng)建、發(fā)送、接收、和讀取消息的JAVA技術(shù)。
代碼示例:
try{ Properties env = new Properties(); InitialContext inictxt = new InitialContext(env); TopicConnectionFactory connFactory = (TopicConnectionFactory) inictxt.lookup("TTopicConnectionFactory"); ... } catch(NamingException ne){ ... }
訪問特定目錄:舉個(gè)例子,人是個(gè)對(duì)象,他有好幾個(gè)屬性,諸如這個(gè)人的姓名、電話號(hào)碼、電子郵件地址、郵政編碼等屬性。通過getAttributes()方法
Attribute attr = directory.getAttributes(personName).get("email"); String email = (String)attr.get();
通過使用JNDI讓客戶使用對(duì)象的名稱或?qū)傩詠聿檎覍?duì)象:
foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls);
通過使用JNDI來查找諸如打印機(jī)、數(shù)據(jù)庫(kù)這樣的對(duì)象,查找打印機(jī)的例子:
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);――綁定:把名稱同對(duì)象關(guān)聯(lián)的過程
- void rebind(String sName,Object object);――重新綁定:用來把對(duì)象同一個(gè)已經(jīng)存在的名稱重新綁定
- void unbind(String sName);――釋放:用來把對(duì)象從目錄中釋放出來
- void lookup(String sName,Object object);――查找:返回目錄總的一個(gè)對(duì)象
- void rename(String sOldName,String sNewName);――重命名:用來修改對(duì)象名稱綁定的名稱
- NamingEnumeration listBinding(String sName);――清單:返回綁定在特定上下文中對(duì)象的清單列表
- NamingEnumeration list(String sName);
代碼示例:重新得到了名稱、類名和綁定對(duì)象。
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(); }
了解名字服務(wù)和目錄服務(wù)的相關(guān)概念,有助于更好的使用JNDI。 Naming service 名字服務(wù)定義了如何將名字與對(duì)象關(guān)聯(lián),并通過名字如何找到對(duì)象的方法。典型的例子如:DNS將域名與IP關(guān)聯(lián),文件系統(tǒng)將文件名與文件相關(guān)聯(lián)。在名字服務(wù)中,主要的概念:
- 名字(Names),在名字系統(tǒng)中實(shí)際對(duì)象的代號(hào),如文件名,域名等,它會(huì)被用來查找關(guān)聯(lián)的對(duì)象。不同的系統(tǒng)中會(huì)有不同的命名規(guī)范,如文件系統(tǒng)采用“\”來表示層級(jí),而DNS則使用“.”。
- 綁定(Bindings),名字和實(shí)際對(duì)象的關(guān)聯(lián)。
- 引用和地址(References and Addresses),當(dāng)對(duì)象不能直接被存儲(chǔ)在名字系統(tǒng)時(shí),就必須使用引用,通過引用找到實(shí)際的對(duì)象。在系統(tǒng)中,保存的引用的內(nèi)容被稱為地址。引用還有另一個(gè)用處:在名字系統(tǒng)中,缺少象關(guān)系數(shù)據(jù)庫(kù)中外鍵的概念。通過使用引用,可以作為外鍵的一個(gè)取代辦法。
- 上下文(Context),它是一個(gè)名字-對(duì)象集合,提供了與名字系統(tǒng)交互的主要操作,如查找、綁定、去綁定。子上下文(subcontext)與它的關(guān)系類似文件系統(tǒng)中目錄和子目錄的關(guān)系,子上下文被包含在一個(gè)上下文中,通過父上下文中的一個(gè)名字與子上下文關(guān)聯(lián)。
- 名字系統(tǒng)和名字空間(Naming Systems and Namespaces),名字系統(tǒng)是相同類型的上下文的集合,它提供名字服務(wù);名字空間,是名字系統(tǒng)中的名字集合,如文件系統(tǒng)的文件名和目錄。
Directory service 目錄服務(wù)是名字服務(wù)的擴(kuò)展,它除了關(guān)聯(lián)名字和對(duì)象,還允許對(duì)象包含屬性。目錄系統(tǒng)通常以層次結(jié)構(gòu)組織數(shù)據(jù)。在目錄服務(wù)中的主要概念:
- 屬性(Attributes),它屬于目錄對(duì)象,它是(名字,值)對(duì),屬性可以有多個(gè)值。
- 目錄和目錄服務(wù)(Directories and Directory Services),目錄是目錄對(duì)象的集合;目錄服務(wù)則提供與目錄相關(guān)的服務(wù),創(chuàng)建、刪除和修改存放在目錄中的對(duì)象的屬性。
- 查找和查找過濾器(Searches and Search Filters),獲取目錄對(duì)象的操作就是查找;過濾器是類似查找條件的對(duì)象。
基本使用
² 注冊(cè)JNDI提供者
在使用JNDI之前,需要先獲取JNDI的提供者,并在系統(tǒng)注冊(cè)它。與JNDI相關(guān)的系統(tǒng)屬性在javax.naming.Context中定義,常用的屬性:
- java.naming.factory.initial,服務(wù)提供者用來創(chuàng)建InitialContext的類名。
- java.naming.provider.url,用來配置InitialContext的初始url
- java.naming.factory.object,用來創(chuàng)建name-to-object映射的類,用于NameClassPair和References。
- java.naming.factory.state,用來創(chuàng)建jndi state的類
對(duì)于目錄服務(wù),由于一般需要安全設(shè)置,還通常使用:
- java.naming.security.authentication,安全類型,三個(gè)值:none,simple或strong。
- java.naming.security.principal,認(rèn)證信息。
- java.naming.security.credentials,證書信息。
- java.naming.security.protocol,安全協(xié)議名。
使用System.setProperty注冊(cè),如果程序不顯示說明,那么java會(huì)在classpath內(nèi)查找jdni.properties文件來完成注冊(cè)。jdni.properties例子:
java.naming.factory.initial=com.codeline.db.MockInitialContextFactory
連接服務(wù)
注冊(cè)之后,就可以實(shí)施服務(wù)連接了。對(duì)于名字服務(wù)由InitialContext開始,目錄服務(wù)則使用InitialDirContext。它們分別實(shí)現(xiàn)了Context和DirContext,這兩個(gè)接口分別對(duì)應(yīng)名字服務(wù)和目錄服務(wù)的接口,也是JNDI中最重要的兩個(gè)接口。
連接名字服務(wù):
System.setProperty(Context.INITIAL_CONTEXT_FACTORY," com.sun.jndi.fscontext.FSContextFactory"); InitialContext ctx = new InitialContext();
連接目錄服務(wù):
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);
多服務(wù)提供者:如果應(yīng)用包含多個(gè)服務(wù)提供者,在連接時(shí)略有不同。以名字服務(wù)為例
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"); //使用不同的構(gòu)造函數(shù) InitialContext ctx = new InitialContext(env);
查找對(duì)象
不論名字服務(wù)還是目錄服務(wù),都是使用lookup來查找對(duì)象的。除了可以使用String作為參數(shù)之外,lookup還可使用Name接口作為參數(shù)。
Greeter greeter = (Greeter)ctx.lookup("SayHello");
如果想要獲得上下文中所有的對(duì)象名字,就使用lis返回NameClassPair列表。NameClassPair包含對(duì)象名字和對(duì)象類名。如果想要獲得實(shí)際的對(duì)象實(shí)例列表,就使用listBindings,它返回Binding列表。Binding是NameClassPair的子類,它包含對(duì)象的實(shí)例。
- 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()); }
對(duì)象綁定
- 使用bind添加綁定
Fruit fruit = new Fruit("orange"); ctx.bind("favorite", fruit);
- 使用rebind修改綁定
Fruit fruit = new Fruit("lemon"); ctx.rebind("favorite", fruit);
- 使用unbind去除綁定。
ctx.unbind("favorite");
對(duì)象改名
使用rename可以給一個(gè)在上下文中的對(duì)象改名
ctx.rename("report.txt", "old_report.txt");
- 獲取屬性
屬性相關(guān)的接口是Attribute和Attributes,它們都在javax.naming.directory包內(nèi)。通過DirContext的getAttributes方法就可以獲得對(duì)象的屬性集合,然后使用Attributes的get方法獲得對(duì)應(yīng)的屬性,最后通過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的所有屬性,在實(shí)際使用過程中,考慮網(wǎng)絡(luò)帶寬的影響,可以設(shè)置獲取要獲取的屬性列表:
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) { //構(gòu)造條件 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方法完成。所謂的修改過程,實(shí)際就是先構(gòu)造要修改的屬性列表,然后使用上述方法提交。對(duì)于屬性包含多個(gè)值時(shí),需要把屬性的不修改的值也要包含,否則服務(wù)器會(huì)認(rèn)為那些值不再需要而刪除它們。
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,也可一次進(jìn)行多個(gè)不同的修改操作:
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);
創(chuàng)建上下文
使用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);
實(shí)例
以下再舉一個(gè)實(shí)例。
在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中聲明了一個(gè)組件,接下來在代碼中可以獲取這個(gè)組件:
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(); }
總結(jié):在tomcat中配置jndi組件,然后在代碼中獲取已配好的組件。
各WEB容器的JNDI實(shí)現(xiàn)類是不同的,比如在JBOSS中,JNDI提供類是org.jnp.interfaces.NamingContextFactory,與tomcat是不同的。
這樣看來,JNDI的作用和spring的依賴注入倒是差不多。但是通過JNDI,可以實(shí)現(xiàn)跨應(yīng)用,甚至跨域獲取組件。在服務(wù)器A上配置的組件,在另一臺(tái)服務(wù)器B上,可以通過JNDI獲取到。
spring也提供了對(duì)jndi的封裝,使用起來更加方便,以下是一個(gè)例子。
<!-- 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> <!-- 創(chuàng)建連接工廠 --> <bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiTemplate" ref="jndiTemplate" /> <property name="jndiName" value="TopicConnectionFactory" /> </bean>
先聲明JndiTemplate,配置好目標(biāo)地址、JNDI服務(wù)提供類。然后通過JndiObjectFactoryBean,就可以很方便地獲取JNDI組件,并進(jìn)行類型轉(zhuǎn)換。
相關(guān)文章
MybatisX-Generator自動(dòng)代碼生成插件教程
這篇文章主要介紹了MybatisX-Generator自動(dòng)代碼生成插件教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04SSH框架網(wǎng)上商城項(xiàng)目第14戰(zhàn)之商城首頁(yè)UI的設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第14戰(zhàn)之商城首頁(yè)UI的設(shè)計(jì),感興趣的小伙伴們可以參考一下2016-06-06詳解Maven profile配置管理及激活profile的幾種方式
這篇文章主要介紹了詳解Maven profile配置管理及激活profile的幾種方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01spring boot實(shí)現(xiàn)超輕量級(jí)網(wǎng)關(guān)的方法(反向代理、轉(zhuǎn)發(fā))
這篇文章主要介紹了spring boot實(shí)現(xiàn)超輕量級(jí)網(wǎng)關(guān)(反向代理、轉(zhuǎn)發(fā))的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11java 二維數(shù)組矩陣乘法的實(shí)現(xiàn)方法
java 二維數(shù)組矩陣乘法的實(shí)現(xiàn)方法,需要的朋友可以參考一下2013-03-03通過Spring Boot + Mybatis + Redis快速搭建現(xiàn)代化Web項(xiàng)目
本篇文章介紹了如何通過Spring Boot、Mybatis以及Redis快速搭建一個(gè)現(xiàn)代化的Web項(xiàng)目,并且同時(shí)介紹了如何在Spring Boot下優(yōu)雅地書寫單元測(cè)試來保證我們的代碼質(zhì)量。具體內(nèi)容詳情大家通過本文學(xué)習(xí)下吧2017-12-12java中用float時(shí),數(shù)字后面加f,這樣是為什么你知道嗎
這篇文章主要介紹了java用float時(shí),數(shù)字后面加f,這樣是為什么你知道嗎?具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09