Java之JNDI詳解


JNDI的基本應用
        JNDI是Java Naming and Directory Interface(JAVA命名和目錄接口)的英文簡寫,它是為JAVA應用程序提供命名和目錄訪問服務的API(Application Programing Interface,應用程序編程接口)。

1.命名的概念與應用
        JNDI中的命名(Naming),就是將Java對象以某個名稱的形式綁定(binding)到一個容器環境(Context)中,以后調用容器環境(Context)的查找(lookup)方法又可以查找出某個名稱所綁定的Java對象。讀者也許會感到奇怪:自己創建一個Java對象,將其綁定到JNDI容器環境中后又查詢出來,這有什么意思?在真實的項目應用中,通常是由系統程序或框加程序先將資源對象綁定到JNDI環境中,以后在該系統或框架中運行的模塊程序就可以從JNDI環境中查找這些資源對象了。例如,Tomcat服務器在啟動時可以創建一個連接到某種數據庫系統的數據源(DataSource)對象,並將該數據源(DataSource)對象綁定到JNDI環境中,以后在這個Tomcat服務器中運行的Servlet和JSP程序就可以從JNDI環境中查詢出這個數據源(DataSource)對象進行使用,而不用關心數據源(DataSource)對象是如何創建出來的,這種方式極大地增強了系統的可維護性,當數據庫系統的連接參數發生變更時,這只是Tomcat系統管理員一個人要關心的事情,而與所有的應用程序開發人員無關。
        容器環境(Context)本身也是一個Java對象,它也可以通過一個名稱綁定到另一個容器環境(Context)中。將一個Context對象綁定到另外一個Context對象中,這就形成了一種父子級聯關系,多個Context對象最終可以級聯成一種樹狀結構,樹中的每個Context對象中都可以綁定若干個Java對象,如圖6.10所示。


圖6.10
        圖6.10中的每個方框分別代表一個Context對象,它們綁定的名稱分別為a、b、c、d、e,b和c是a的子Context,d是b的子Context,e又是d的子Context。圖9.x中的各個方框內的每個小橢圓分別代表一個Java對象,它們也都有一個綁定的名稱,這些綁定名稱分別為dog、pig、sheet等,在同一個Context不能綁定兩個相同名稱的Java對象,在不同的Context中可以出現同名的綁定對象。可見,Context樹的級聯結構與文件系統中的目錄結構非常類似,Context與其中綁定的Java對象的關系也非常類似於文件系統中的目錄與文件的關系。從圖6.10中可以看到,要想得到Context樹中的一個Java對象,首先要得到其所在的Context對象,只要得到了一個Context對象,就可以調用它的查詢(lookup)方法來獲得其中綁定的Java對象。另外,調用某個Context對象的lookup方法也可以獲得Context樹中的任意一個Context對象,這只需要在lookup方法中指定相應的Context路徑即可。在JNDI中不存在着“根”Context的概念,也就是說,執行JNDI操作不是從一個“根”Context對象開始,而是可以從Context樹中的任意一個Context開始。無論如何,程序必須獲得一個作為操作入口的Context對象后才能執行各種JNDI命名操作,為此,JNDI API中提供了一個InitialContext類來創建用作JNDI命名操作的入口Context對象。Context是一個接口,Context對象實際上是Context的某個實現類的實例對象,選擇這個具體的Context實現類並創建其實例對象的過程是由一個Context工廠類來完成的,這個工廠類的類名可以通過JNDI的環境屬性java.naming.factory.initial指定,也可以根據Context的操作方法的url參數的Schema來選擇。

2.目錄的概念與應用
        JNDI中的目錄(Directory)與文件系統中的目錄概念有很大的不同,JNDI中的目錄(Directory)是指將一個對象的所有屬性信息保存到一個容器環境中。JNDI的目錄(Directory)原理與JNDI的命名(Naming)原理非常相似,主要的區別在於目錄容器環境中保存的是對象的屬性信息,而不是對象本身,所以,目錄提供的是對屬性的各種操作。事實上,JNDI的目錄(Directory)與命名(Naming)往往是結合在一起使用的,JNDI API中提供的代表目錄容器環境的類為DirContext,DirContext是Context的子類,顯然它除了能完成目錄相關的操作外,也能完成所有的命名(Naming)操作。DirContext是對Context的擴展,它在Context的基礎上增加了對目錄屬性的操作功能,可以在其中綁定對象的屬性信息和查找對象的屬性信息。JNDI中的目錄(Directory)的結構示意圖如圖6.11所示。


圖6.11
        圖6.11中的每個最外層的方框分別代表一個DirContext對象,它們綁定的名稱分別為a、b,b是a的子DirContext。圖6.11中的各個最外層的方框內的每個小橢圓分別代表一個Java對象,各個里層的方框分別代表一個對象的屬性。從名稱為a的DirContext中的內容可以看到,一個DirContext容器環境中即可以綁定對象自身,也可以綁定對象的屬性信息,綁定的對象和綁定的屬性是完全獨立的兩個事物,即使它們的綁定名稱相同,它們的操作也是完全獨立的。另外,一個屬性可以有多個屬性值,例如,dog對象的category屬性就設置了兩個屬性值:meat和pet。從名稱為b的DirContext中的內容可以看到,一個DirContext容器環境中也可以只綁定對象的屬性信息,而不綁定任何對象自身。與Context的操作原理類似,JNDI API中提供了一個InitialDirContext類來創建用作JNDI命名與目錄屬性操作的入口DirContext對象。

3. 用於DNS查詢的JNDI服務程序
        JNDI API是面向應用程序開發人員的編程接口,它在運行時需要調用某個具體的JNDI服務程序,JNDI API與JNDI服務程序之間的關系,猶如JDBC與JDBC驅動程序之間的關系。從JDK 1.3開始,JDK中就集成了JNDI API,從JDK 1.4開始的版本又集成了用於DNS查詢的JNDI服務程序,所以,如果我們使用JDK 1.4及更高的JDK版本來開發DNS信息查詢程序時,不需要下載和安裝JNDI API和用於DNS查詢的JNDI服務程序。SUN公司提供的用於查詢DNS信息的JNDI服務程序,將某個域名的DNS信息以屬性的形式綁定到代表該域名的DirContext對象上。打開JDK幫助文檔的首頁,在其中搜索“jndi”關鍵字,可以看到一條“jndi”的超鏈接,單擊這個超鏈接,就可以進入“Java Naming and Directory Interface”的幫助頁面,如圖6.12所示。


圖6.12
        單擊圖6.12中的“The DNS Service Provider”超鏈接,進入DNS服務程序的幫助頁面。只要我們具備JDNI編程的一些基本知識,再加上該幫助文檔頁提供的信息,我們就知道如何調用這個DNS服務程序來獲得某個域的DNS信息和MX記錄了。

        下面編寫一個試驗性的JNDI程序,這個程序用於幫助我們熟悉和掌握JNDI API的使用,也幫助我們了解DNS的JNDI服務程序以怎樣的形式返回DNS信息。

動手實踐:使用JNDI API獲取DNS信息
        按例程6-5編寫一個名為DNSQuery.java的程序,這個程序使用JNDI API來獲得某個域的DNS信息,並從中提取出域的一台SMTP服務器的名稱,其中的很多代碼都是為了幫助我們熟悉JNDI API的使用和了解DNS的JNDI服務程序返回的DNS信息內容而加入的。運行這個程序時,需要指定一個或兩個參數,第一個參數是必須的,為要查詢的域名,第二個參數是可選的,為查詢時所使用的DNS服務器的IP地址,如果沒有指定第二個參數,DNS的JNDI服務程序將使用底層操作系統上設置的DNS服務器。

例程6-5 DNSQuery.java

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public class DNSQuery 
{
    public static void main(String[] args) throws NamingException 
    {
        /*第一個參數指定要查詢的域或主機名,第二個參數指定查詢的DNS服務器,
        為了程序的簡單易讀性,省略了嚴格的參數錯誤檢查*/
        String domain = args[0];
        String dnsServer = args.length<2 ? "" : ("//" + args[1]);

        //通過環境屬性來指定Context的工廠類
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
                    "com.sun.jndi.dns.DnsContextFactory");
        env.put(Context.PROVIDER_URL, "dns:" + dnsServer);
        DirContext ctx = new InitialDirContext(env);
        //分別獲取包含所有屬性和只包含Mx屬性的Attributes對象
        Attributes attrsAll = ctx.getAttributes(domain);    
        Attributes attrsMx = ctx.getAttributes(domain, new String[]{"MX"}); 

        /*上面的整段程序代碼也可以用下面這段程序代碼來替代,下面這段程序
        代碼通過查詢URL中的Schema信息來自動選擇Context的工廠類*/
        /*
        DirContext ctx = new InitialDirContext();
        Attributes attrsAll = ctx.getAttributes("dns:" + dnsServer + "/" + domain);
        Attributes attrsMx = ctx.getAttributes(
            "dns:" + dnsServer + "/" + domain, new String[]{"MX"});         
        */

        System.out.println("打印出域" + domain + 
                                "的Attributes對象中的信息:");
        System.out.println(attrsAll);
        System.out.println("--------------------------");
        System.out.println("打印只檢索域" + domain + 
                                "的MX記錄的Attributes對象:");     
        System.out.println(attrsMx);

        System.out.println("--------------------------");
        System.out.println("逐一打印出Attributes對象中的各個屬性:");         
        NamingEnumeration attributes = attrsAll.getAll();
        while(attributes.hasMore()) 
        {   
            System.out.println(attributes.next());
        }

        System.out.println("--------------------------");
        //直接調用get方法從attrsMx集合檢索MX屬性
        System.out.println("直接檢索Attributes對象中的MX屬性:");      
        Attribute attrMx = attrsAll.get("MX");
        System.out.println(attrMx); 

        System.out.println("--------------------------");           
        //獲取Mx屬性中的第一個值:
        System.out.println("獲取Mx屬性中的第一個值:");
        String recordMx = (String)attrMx.get();
        System.out.println(recordMx);
        //從Mx屬性的第一個值中提取郵件服務器地址
        System.out.println("從MX屬性值中提取的郵件服務器地址:");
        String smtpServer = recordMx.substring(
                            recordMx.indexOf(" ") + 1);
        System.out.println(smtpServer);
}

 

(2)在Windows命令行窗口中編譯DNSQuery.java程序后,接着在命令行窗口中執行如下命令:

ipconfig /all

如果ipconfig命令顯示的結果中包含有DNS Server的信息,那么我們接着就可以使用如下命令來啟動執行DNSQuery類:

java  DNSQuery  sina.com 

上面的命令的運行結果如圖6.13。


圖6.13
()假設在上一步用ipconfig命令查看到的本地計算機上配置的DNS Server為202.106.46.151,那么,我們接着執行如下命令:

java  DNSQuery  sina.com 202.106.46.151

這個命令執行完后,也能顯示出圖6.13中的信息。我們接着故意將上面命令中的DNS服務器參數指定為一個錯誤的IP地址進行執行,修改后的命令語句如下所示:

java  DNSQuery  sina.com 192.168.1.151

這個命令執行完后的結果如圖6.14所示:

圖6.14
如果計算機只能通過代理服務器連接到Internet,那么在該計算機上直接執行如下命令:

java  DNSQuery  sina.com 

這也將導致圖6.14中的錯誤。如果要想在通過代理服務器上網的情況下,正確執行上面的程序,可以采用如下命令:

java -DsocksProxyHost=162.105.1.200 -DsocksProxyPort=808 DNSQuery sina.com 202.106.46.151

由於上面的命令太長,在排版時分成了兩行來書寫,讀者在輸入上面這條命令時,不要手工換行。讀者應該根據自己的實際情況,修改其中的代理服務器地址、代理端口號和DNS服務器的地址。


免責聲明!

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



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