1. 程式人生 > >JNDI學習總結(一):JNDI到底是什麼?

JNDI學習總結(一):JNDI到底是什麼?

首先我們來回顧一下簡單的問題,列在下面第一點。
1.我們知道,Java 的執行從 static main 開始,為什麼一定要從 static 方法開始呢?
2.在我們知道這個世界上的另外一個地方有一個物件存在而且伺服器也會在我們開始工作前為我們準備好,那麼我該怎麼找到它呢?如果這個物件是我這個類建立的,那麼當然簡單,直接用物件的引用就能呼叫它的方法,那如果這個物件不是我建立的,我想主動呼叫它的方法這似乎在任何程式語言中都不可能,記得寫一個方法那是被別人呼叫的不是主動呼叫別人。

就像你找人一樣,如果他還沒有和你建立聯絡的話(建立聯絡就是儲存一份物件的引用,如果兩個物件彼此沒有建立另外一個而且也沒有被中間的第三方建立這種關係問題就出現了),請問你如何和他打交道?
現實中是:
a. 我們撥通 114  
  Java 中:Context ctx = new InitialContext();
b. 請問哪裡有通馬桶的?114 答,xxx... 為您轉接中,請稍候。  
  Java 中:DataSource ds = (DataSource) ctx.lookup("便民服務公司");
c. 過了一會兒,人來了,你說:師付,請幫我通馬桶吧。
  Java 中: ds.getConnection();

上面的話,我沒有回答你什麼是 JNDI, 但是我回答了為什麼我們需要 JNDI. 希望你在概念上了解了它存在的必要性。

下面的話,給你一點指導如何更好的理解 JNDI 實現:
1.一個物件如果它在另外一個地方(可能與當前執行的程式不在同一個 VM / 同一程序中), 物件怎麼可能從一個 VM 中傳送到另外一個 VM 中呢?像 LDAP 這種,物件的狀態還需要持久地儲存的話(重啟伺服器程序後它還在),又該怎麼辦呢?請看 JNDI StateFactory, 它用一種方法把一個物件轉換成某種方式儲存下來,就像我們把一個 Entity 物件儲存下來時,我們會用 SQL 來做一樣。

2.有一個物件上次已經儲存了狀態,現在伺服器重啟了,上次的物件肯定不在記憶體裡面,我們怎麼恢復上次的狀態呢?
請看 JNDI ObjectFactory. 它讀取一些上次儲存的狀態資訊,來建立並初始化一個物件。比如:我們配置了一個 XML,它是某個 JDBC 資料來源的配置資料,Application Server 啟動時讀取這個資訊(相當於上次的狀態),然後重啟物件。

3.企業應用這麼複雜,面向介面程式設計,那如何用一種簡單的方式來配置新的實現類呢?Java 的做法是:
已經定義了 SPI (Service Provider Interface). 包括以下幾點:
  介面準備好了,如:StateFactory / ObjectFactory.
  配置:先搜尋 JRE 下面的某個 jndiprovider.properties 檔案當作預設實現,再查詢使用者 classpath 根路徑下 /jndi.properties. 另外還有 System.getProperties() 和在建立 InitialContext 給一個 hashtable 作為引數,這三個引數, 有優先順序的關係,越是後面具體的引數優先順序越高,越前面越通用型的引數優先順序越低。這一點,請看 JDK ResourceManager 這個類的原始碼。
  實現類與初始化它們是如何自動完成的呢?這個你需要看 Context 接口裡面的常量,以及拿 Sun LDAP InitialContextFactory 執行樣例來看 Context 介面的常量一個樣本引數值,一般我們很重要的是 InitialContextFactory 這個引數,但也有時候也有其他引數要配置,比如:pkgs, 它是說,我們給一個包名,JNDI 管理器要查詢實現時用這個包名列表當成包名,類名就是 協議名 + 固定的字尾:比如: ldap://localhost:389, 它會用一個'包名字首.協議名.協議名 + URLContextFactory' 作為類名來搜尋一個類,如果它存在就把它當成實現類,如果沒找到再嘗試另外一個包名字首。你可以看 com.sun.jndi.url 名,下面有例子看,比如說 ldap:// 的情況就是 找一個類 com.sun.jndi.url.ldap.ldapURLContextFactory,如果是 dns://www.163.com/xxx 就找個 com.sun.jndi.url.dns.dnsURLContextFactory。這是 URL context factory 也就是當你使用 ctx.lookup("java:xxxx/yyy") 這種帶協議字首的時候。

另外你也可以類比地看 com.sun.www.protocol 包裡面的類,它是另外不一個與 JNDI 不相關的 URLStreamHandler 處理的規則,與些設計和配置幾乎完全相同。我以前寫過一個 jdbc:oracle:username/password:@localhost:1521:training/[select A from C where DEL_IND = 0] , 在 java 程式中輸入這個 URL 我們可以把資料庫裡面的資料讀取出來,效果就根你輸入 file:/C:/boot.ini 讀取了這個檔案內容一樣,辦法就是我寫了一個支援 jdbc 協議的 URLStreamHandler 在命令列配置一個使用它,其他的應用程式類就能自動處理,它們都不知道我是從資料庫裡面讀取的資料。


4. J2EE 1.3 開始,資源的管理由應用伺服器單獨來管理和配置,這與 J2EE 1.2 不同,在 J2EE 1.2 中我們直接在應用程式中配置我們要用的資源。J2EE 1.3 中我們配置一個數據源在伺服器上,我們在應用程式中只需要說明我們配置的資源的引用就行了,比如我們只在 web.xml 或 ejb-jar.xml 配置 <resource-ref /> 而不是 data source 本身。這有什麼好處?比如:我們定義了兩個 training 的資料來源:jdbc/training/db2. jdbc/oracle/db2. 一個是開發環境,一個是 UAT 環境,現在開發時我們建立一個 <resource-ref /> 指向jdbc/training/db2,那麼就用 db2 資料庫,UAT 測試時我們建立另外一個 <resource-ref /> 指向 jdbc/training/oracle, 就會使用 oracle 資料庫,而這本身不需要修改程式碼,只是修改了 web.xml / ejb-jar.xml ,而且現在連線到資料的使用者名稱和密碼不再是應用程式開發本身的事情,因為你不需要配置資源也就不需要知道它的登入名和密碼,而是由管理員在伺服器上配置資料來源,這裡注意,開發人員做他程式碼部分的事情,伺服器管理員負責配置資源源,J2EE component provider 和 Deployer 兩個角色的職責分開了,雖然現實中 deployer 都是委託給了開發人員,但 J2EE 規範是分開來描述的。

5.上面說了半天,目的是什麼呢?這是我的痛苦經歷,第一次寫 EJB, 買了本書,J2EE 從入門到精通(就是那本傳說中的黃皮寶典系列),寫了一個無狀態 session bean 來訪問資料來源,死活找不到資料來源:NamingException: xxx not found. 在 IBM developerworks 上看到一篇文章,茅塞頓開,原來那本書講的是J2EE 1.2, 我用的 WSAD 5.1.2 開發用的預設配置都是 J2EE 1.3。這裡面引出了 JNDI LinkRef, 為了實現上面 4 裡面所說的伺服器上配置一個資源,但應用程式裡面配置一個引用的話,現在的應用伺服器在處理這點JNDI技術實現上基本上都是用 LinkRef 來實現的,這是 JNDI 裡面的一個類。伺服器啟動時會建立一個 jdbc/training/db2 和 jdbc/training/oracle 兩個 DataSource 物件 (用的是 ObjectFactory), 當一個應用程式訪問了準備訪問資料來源時,伺服器檢測到了 web.xml/ejb-jar.xml 中指定了 <resource-ref /> 它就會建立一個 LinkRef 放到 context 中去,它的名稱是:jdbc/training,但它的 ref 是 jdbc/training/db2.
這樣我們 ctx.lookup("java:jdbc/training") 時,java 協議對應的 javaURLContextFactory 會把這個 jdbc/training 物件找出來,在檢測到它是一個 LinkRef 物件時,會自動再用它的 ref 值(這裡是 jdbc/training/db2) 再 lookup 一遍,這下終於找到 jdbc/training/db2 這個 data source 物件。

6.JNDI 裡面還有其他的相關的東西。再結合一個 Reference 概念看,LinkRef 是繼承它的。想再具體的瞭解一個實現細節,請拿一份 apache commons-xxx.jar (名字我忘記了,不過用過 spring / hibernate 來建立資料來源的人可能知道它們用 xxxDataSource 做一個不需要在伺服器上配置,但卻能使用 data source 的辦法),我不是推薦你這個 jar, 我是推薦你看這個xxxDataSource 原始碼,裡面演示了一個 ObjectFactory 用法。這和 JMS ConnectionFactory等其他 J2EE 託管資源的配置和使用都是用的同樣的技術實現的。舉一反三。
想了解更,就再看一個 StateFactory 的實現以及 Reference 的原始碼之類的。作為期望邁入 J2EE 中級程式設計的你,至少在概念和理論上要知道 ObjectFactory / LinkRef / SPI / resource ref 配置這幾點,如果你再知道 StateFactory 是怎麼實現的就更好了。

7.剩下的自學開源專案的原始碼吧。雖然多花時間,但能舉一反三,不用老是 Google 那些灌水貼。