今天跟蹤代碼,發(fā)現(xiàn)在IntialContext的構(gòu)造方法中會調(diào)用System.getProperties(),竟然從中得到了在jndi.properties文件中配置的信息,于是就將InitialContext的API中內(nèi)容又重新讀了一遍。
API中寫道:
JNDI 通過按順序合并取自以下兩個源的值來確定每個屬性值:
- 構(gòu)造方法的環(huán)境參數(shù)、(適當(dāng)屬性的)applet 參數(shù),以及系統(tǒng)屬性中最先出現(xiàn)的屬性。
- 應(yīng)用程序資源文件(jndi.properties)。
對于同時存在于兩個源或多個應(yīng)用程序資源文件中的每個屬性,用以下方式確定屬性值。如果該屬性是指定 JNDI 工廠列表的標(biāo)準(zhǔn)JNDI 屬性之一(參見 Context),則所有值都被串聯(lián)成一個以冒號分隔的列表。對于其他屬性,只使用最先找到的值。
這一定位初始上下文和 URL 上下文工廠的默認(rèn)策略可以通過調(diào)用NamingManager.setInitialContextFactoryBuilder()重寫。
資源文件
要簡化設(shè)置 JNDI 應(yīng)用程序所需環(huán)境的任務(wù),可以將資源文件 與應(yīng)用程序組件和服務(wù)提供程序一起發(fā)布。JNDI資源文件是使用屬性文件格式的文件(參見 java.util.Properties),包括一個鍵/值對列表。鍵是屬性的名稱(例如"java.naming.factory.object"),而值是使用為該屬性定義的格式的字符串。以下是 JNDI資源文件的一個示例:
java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
JNDI 類庫讀取資源文件,并使屬性值隨意可用。因此應(yīng)該認(rèn)為 JNDI資源文件是“所有人可讀的”,敏感信息(比如明文密碼)不應(yīng)該存儲在那里。
有兩種 JNDI 資源文件:提供程序 和應(yīng)用程序。
提供程序資源文件
每個服務(wù)提供程序都有一個可選的資源,該資源列出了特定于該提供程序的屬性。此資源的名稱是:
[prefix/]jndiprovider.properties
其中 prefix 是提供程序的上下文實(shí)現(xiàn)的包名稱,其每個句點(diǎn) (".") 都被轉(zhuǎn)換成一個斜杠 ("/")。例如,假設(shè)服務(wù)提供程序定義了一個帶有類名稱 com.sun.jndi.ldap.LdapCtx的上下文實(shí)現(xiàn)。此提供程序的提供程序資源被命名為 com/sun/jndi/ldap/jndiprovider.properties。如果該類不在一個包中,則資源的名稱就是jndiprovider.properties。
JNDI 類庫中的某些方法使用指定 JNDI工廠列表的標(biāo)準(zhǔn) JNDI 屬性:
在確定這些屬性的值時,JNDI庫將參考提供程序資源文件。這以外的屬性可由服務(wù)提供程序在提供程序資源文件中設(shè)置。服務(wù)提供程序的文檔應(yīng)該明確聲明哪些屬性是被允許的;文件中的其他屬性將被忽略。
應(yīng)用程序資源文件
在部署應(yīng)用程序時,該應(yīng)用程序通常將在其類路徑中生成若干代碼基目錄和 JAR。類似地,在部署 applet 時,它將有一個指定applet 類所處地址的代碼基和檔案文件。JNDI查找(使用 ClassLoader.getResources())類路徑中所有名為jndi.properties 的應(yīng)用程序資源文件。此外,如果文件java.home/lib/jndi.properties 存在并且是可讀的,則 JNDI會將其視為一個額外的應(yīng)用程序資源文件。(java.home 指示由 java.home系統(tǒng)屬性命名的目錄。)包含在這些文件中的所有屬性都被放置在初始上下文環(huán)境中。然后此環(huán)境由其他上下文繼承。
對于同時出現(xiàn)在多個應(yīng)用程序資源文件中的每個屬性,JNDI使用最先找到的值,或者在少數(shù)有意義的情況下串聯(lián)所有這些值(細(xì)節(jié)在下文給出)。例如,如果在三個jndi.properties 資源文件中存在 "java.naming.factory.object"屬性,則對象工廠列表是所有三個文件中的屬性值的串聯(lián)。使用此方案,每個可部署組件都要負(fù)責(zé)列出它導(dǎo)出的工廠。JNDI在搜索工廠類時自動收集和使用所有這些導(dǎo)出列表。
從 Java 2 Platform 開始可使用應(yīng)用程序資源文件,java.home/lib中的文件除外,它在較早的 Java 平臺上也可以使用。
屬性的搜索算法
當(dāng) JNDI 構(gòu)造一個初始上下文時,該上下文的環(huán)境是使用傳遞給構(gòu)造方法的環(huán)境參數(shù)中定義的屬性、系統(tǒng)屬性、applet參數(shù)和應(yīng)用程序資源文件進(jìn)行初始化的。有關(guān)細(xì)節(jié)請參見 InitialContext。然后此初始環(huán)境由其他上下文實(shí)例繼承。
如果 JNDI 類庫需要確定某一屬性的值,它將通過按順序合并取自以下兩個源的值來實(shí)現(xiàn)這一點(diǎn):
- 將在其上執(zhí)行操作的上下文的環(huán)境。
- 將在其上執(zhí)行操作的上下文的提供程序資源文件 (jndiprovider.properties)。
對于每個同時存在于這兩個源中的屬性,JNDI 用以下方式確定屬性的值。如果該屬性是指定 JNDI 工廠列表的標(biāo)準(zhǔn) JNDI屬性之一(如上文所列),則這些值被串聯(lián)成一個以冒號分隔的列表。對于其他屬性,只使用最先找到的值。
當(dāng)服務(wù)提供程序需要確定某一屬性的值時,它通常將直接從環(huán)境中獲取該值。服務(wù)提供程序可以定義將置于其本身提供程序資源文件中的特定于提供程序的屬性。在這種情況下,它應(yīng)該根據(jù)上文所述合并這些值。
這樣,每個服務(wù)提供程序開發(fā)人員便可以指定與該服務(wù)提供程序一起使用的工廠列表。這可以由應(yīng)用程序或 applet的部署方指定的應(yīng)用程序資源修改,而這些資源又可以由用戶修改。
如上即為所查API關(guān)于jndi.properties文件的全部說明??偨Y(jié)起來即為:
jndi.properties是jndi初始化文件。通常我們有兩種方式來創(chuàng)建一個初始上下文:
1.通過創(chuàng)建一個Properties對象,設(shè)置Context.PROVIDER_UR,Context.InitialContextFactroy等等屬性,創(chuàng)建InitialContext,例如:
Propertiesp=new Properties();
p.put(Cotnext.PROVIDER_URL, "localhost:1099 ");//主機(jī)名和端口號
//InitialContext的創(chuàng)建工廠類(類名是我亂寫的)
p.put(Context.InitialContextFactroy, "com.sun.InitialContextFactory");
InitialContextctx=new InitialContext(p);
2.通過jndi.properties文件創(chuàng)建初始上下文
java.naming.factory.initial=com.sun.NamingContextFactory
java.naming.provider.url=localhost:1099
如果直接創(chuàng)建初始上下文,如下:
InitialContextctx=new InitialContext();
InitialContext的構(gòu)造器會在類路徑中找jndi.properties文件,如果找到,通過里面的屬性,創(chuàng)建初始上下文。
所以從上面可以看出,兩種方式完成的目標(biāo)是相同的。
因此,對于本地測試(并且JNDI資源沒有設(shè)置安全屬性)可以不添加properties屬性,但是如果要訪問遠(yuǎn)程的JNDI資源,就必須用飽含JNDI環(huán)境參數(shù)Hashtable初始化InitialContext。
必要的環(huán)境參數(shù)如:
Context.INITIAL_CONTEXT_FACTORY//連接工廠
Context.PROVIDER_URL//訪問連接
Context.SECURITY_PRINCIPAL//安全用戶
Context.SECURITY_CREDENTIALS//用戶密碼
有時會出現(xiàn)如NoInitialContextException是因?yàn)闊o法從System.properties中獲得必要的JNDI參數(shù)中獲得必要的JNDI參數(shù),在服務(wù)器環(huán)境下,服務(wù)器啟動時就把這些參數(shù)放到System.properties中了,于是直接newInitialContext()就搞定了,不要搞env那么麻煩,搞了env你的代碼還無法移植,弄不好管理員設(shè)置服務(wù)器用的不是標(biāo)準(zhǔn)端口還照樣拋異常。
但是在單機(jī)環(huán)境下,可沒有JNDI服務(wù)在運(yùn)行,那就手動啟動一個JNDI服務(wù)。我在JDK5的rt.jar中一共找到了4種SUN自帶的JNDI實(shí)現(xiàn):LDAP,CORBA,RMI,DNS。
這4種JNDI要正常運(yùn)行還需要底層的相應(yīng)服務(wù)。一般我們沒有LDAP或CORBA服務(wù)器,也就無法啟動這兩種JNDI服務(wù),DNS用于查域名的,以后再研究,唯一可以在main()中啟動的就是基于RMI的JNDI服務(wù)。
現(xiàn)在我們就在main()中啟動基于RMI的JNDI服務(wù)并且綁一個Date對象到JNDI上:
LocateRegistry.createRegistry(1099);
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
System.setProperty(Context.PROVIDER_URL,"rmi://localhost:1099");
InitialContext ctx = new InitialContext();
class RemoteDate extends Date implements Remote {};
ctx.bind("java:comp/env/systemStartTime", new RemoteDate());
ctx.close();
注意,我直接把JNDI的相關(guān)參數(shù)放入了System.properties中,這樣,后面的代碼如果要查JNDI,直接newInitialContext()就可以了,否則,你又得寫Hashtable env = ...
這段話里提到了system.properties屬性,這就是調(diào)用system.getProperties的來由,猜想應(yīng)該是先找到j(luò)ndi.properties文件,之后通過System.setProperties(),而后通過System.getProperties()來得到。
愛華網(wǎng)