JNDI到底是什嗎?

來源:互聯網
上載者:User

JNDI是 Java 命名與目錄介面(Java Naming and Directory Interface),在J2EE規範中是重要的規範之一,不少專家認為,沒有透徹理解JNDI的意義和作用,就沒有真正掌握J2EE特別是EJB的知識。
那麼,JNDI到底起什麼作用?

要瞭解JNDI的作用,我們可以從“如果不用JNDI我們怎樣做?用了JNDI後我們又將怎樣做?”這個問題來探討。

沒有JNDI的做法:
程式員開發時,知道要開發訪問MySQL資料庫的應用,於是將一個對 MySQL JDBC 驅動程式類的引用進行了編碼,並通過使用適當的 JDBC URL 串連到資料庫。
就像以下代碼這樣:

Java code
Connection conn=null;try {  Class.forName("com.mysql.jdbc.Driver",                true, Thread.currentThread().getContextClassLoader());  conn=DriverManager.getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue");    ......  conn.close();} catch(Exception e) {  e.printStackTrace();} finally {  if(conn!=null) {    try {      conn.close();    } catch(SQLException e) {}  }}

這是傳統的做法,也是以前非Java程式員(如Delphi、VB等)常見的做法。這種做法一般在小規模的開發過程中不會產生問題,只要程式員熟悉Java語言、瞭解JDBC技術和MySQL,可以很快開發出相應的應用程式。

沒有JNDI的做法存在的問題:
1、資料庫伺服器名稱MyDBServer 、使用者名稱和口令都可能需要改變,由此引發JDBC URL需要修改;
2、資料庫可能改用別的產品,如改用DB2或者Oracle,引發JDBC驅動程式套件和類名需要修改;
3、隨著實際使用終端的增加,原配置的串連池參數可能需要調整;
4、......

解決辦法:
程式員應該不需要關心“具體的資料庫後台是什嗎?JDBC驅動程式是什嗎?JDBC URL格式是什嗎?訪問資料庫的使用者名稱和口令是什嗎?”等等這些問題,程式員編寫的程式應該沒有對 JDBC 驅動程式的引用,沒有伺服器名稱,沒有使用者名稱稱或口令 —— 甚至沒有資料庫池或串連管理。而是把這些問題交給J2EE容器來配置和管理,程式員只需要對這些配置和管理進行引用即可。

由此,就有了JNDI。

用了JNDI之後的做法:
首先,在在J2EE容器中配置JNDI參數,定義一個資料來源,也就是JDBC引用參數,給這個資料來源設定一個名稱;然後,在程式中,通過資料來源名稱引用資料來源從而訪問後台資料庫。
具體操作如下(以JBoss為例):
1、配置資料來源
在JBoss 的 D:/jboss420GA/docs/examples/jca 檔案夾下面,有很多不同資料庫引用的資料來源定義模板。將其中的 mysql-ds.xml 檔案Copy到你使用的伺服器下,如 D:/jboss420GA/server/default/deploy。
修改 mysql-ds.xml 檔案的內容,使之能通過JDBC正確訪問你的MySQL資料庫,如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
  <jndi-name>MySqlDS</jndi-name>
  <connection-url>jdbc:mysql://localhost:3306/lw</connection-url>
  <driver-class>com.mysql.jdbc.Driver</driver-class>
  <user-name>root</user-name>
  <password>rootpassword</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
  <metadata>
  <type-mapping>mySQL</type-mapping>
  </metadata>
</local-tx-datasource>
</datasources>

這裡,定義了一個名為MySqlDS的資料來源,其參數包括JDBC的URL,驅動類名,使用者名稱及密碼等。

2、在程式中引用資料來源:

Java code
Connection conn=null;try {  Context ctx=new InitialContext();  Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用資料來源  DataSource ds=(Datasource)datasourceRef;  conn=ds.getConnection();    ......  c.close();} catch(Exception e) {  e.printStackTrace();} finally {  if(conn!=null) {    try {      conn.close();    } catch(SQLException e) { }  }}

直接使用JDBC或者通過JNDI引用資料來源的編程代碼量相差無幾,但是現在的程式可以不用關心具體JDBC參數了。
在系統部署後,如果資料庫的相關參數變更,只需要重新設定 mysql-ds.xml 修改其中的JDBC參數,只要保證資料來源的名稱不變,那麼程式原始碼就無需修改。

由此可見,JNDI避免了程式與資料庫之間的緊耦合,使應用更加易於配置、易於部署。

JNDI的擴充:
JNDI在滿足了資料來源配置的要求的基礎上,還進一步擴充了作用:所有與系統外部的資源的引用,都可以通過JNDI定義和引用。

所以,在J2EE規範中,J2EE 中的資源並不局限於 JDBC 資料來源。引用的類型有很多,其中包括資源引用(已經討論過)、環境實體和 EJB 引用。特別是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一項關鍵角色:尋找其他應用程式組件。

EJB 的 JNDI 引用非常類似於 JDBC 資源的引用。在服務趨於轉換的環境中,這是一種很有效方法。可以對應用程式架構中所得到的所有組件進行這類組態管理,從 EJB 組件到 JMS 隊列和主題,再到簡單配置字串或其他對象,這可以降低隨時間的推移服務變更所產生的維護成本,同時還可以簡化部署,減少整合工作。外部資源”。

總結:
J2EE 規範要求所有 J2EE 容器都要提供 JNDI 規範的實現。JNDI 在 J2EE 中的角色就是“交換器” —— J2EE 組件在已耗用時間接地尋找其他組件、資源或服務的通用機制。在多數情況下,提供 JNDI 供應者的容器可以充當有限的資料存放區,這樣管理員就可以設定應用程式的執行屬性,並讓其他應用程式引用這些屬性(Java 管理擴充(Java Management Extensions,JMX)也可以用作這個目的)。JNDI 在 J2EE 應用程式中的主要角色就是提供間接層,這樣組件就可以發現所需要的資源,而不用瞭解這些間接性。

在 J2EE 中,JNDI 是把 J2EE 應用程式合在一起的粘合劑,JNDI 提供的間接定址允許跨企業交付可伸縮的、功能強大且很靈活的應用程式。這是 J2EE 的承諾,而且經過一些計劃和預先考慮,這個承諾是完全可以實現的。

 

說白了就是把資源取個名字,再根據名字來找資源。

//===============================================================================================================

首先我們來回顧一下簡單的問題,列在下面第一點。
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 那些灌水貼。

JNDI是 Java 命名與目錄介面(Java Naming and Directory Interface),在J2EE規範中是重要的規範之一,不少專家認為,沒有透徹理解JNDI的意義和作用,就沒有真正掌握J2EE特別是EJB的知識。
那麼,JNDI到底起什麼作用?

要瞭解JNDI的作用,我們可以從“如果不用JNDI我們怎樣做?用了JNDI後我們又將怎樣做?”這個問題來探討。

沒有JNDI的做法:
程式員開發時,知道要開發訪問MySQL資料庫的應用,於是將一個對 MySQL JDBC 驅動程式類的引用進行了編碼,並通過使用適當的 JDBC URL 串連到資料庫。
就像以下代碼這樣:

Java code
Connection conn=null;try {  Class.forName("com.mysql.jdbc.Driver",                true, Thread.currentThread().getContextClassLoader());  conn=DriverManager.getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue");    ......  conn.close();} catch(Exception e) {  e.printStackTrace();} finally {  if(conn!=null) {    try {      conn.close();    } catch(SQLException e) {}  }}

這是傳統的做法,也是以前非Java程式員(如Delphi、VB等)常見的做法。這種做法一般在小規模的開發過程中不會產生問題,只要程式員熟悉Java語言、瞭解JDBC技術和MySQL,可以很快開發出相應的應用程式。

沒有JNDI的做法存在的問題:
1、資料庫伺服器名稱MyDBServer 、使用者名稱和口令都可能需要改變,由此引發JDBC URL需要修改;
2、資料庫可能改用別的產品,如改用DB2或者Oracle,引發JDBC驅動程式套件和類名需要修改;
3、隨著實際使用終端的增加,原配置的串連池參數可能需要調整;
4、......

解決辦法:
程式員應該不需要關心“具體的資料庫後台是什嗎?JDBC驅動程式是什嗎?JDBC URL格式是什嗎?訪問資料庫的使用者名稱和口令是什嗎?”等等這些問題,程式員編寫的程式應該沒有對 JDBC 驅動程式的引用,沒有伺服器名稱,沒有使用者名稱稱或口令 —— 甚至沒有資料庫池或串連管理。而是把這些問題交給J2EE容器來配置和管理,程式員只需要對這些配置和管理進行引用即可。

由此,就有了JNDI。

用了JNDI之後的做法:
首先,在在J2EE容器中配置JNDI參數,定義一個資料來源,也就是JDBC引用參數,給這個資料來源設定一個名稱;然後,在程式中,通過資料來源名稱引用資料來源從而訪問後台資料庫。
具體操作如下(以JBoss為例):
1、配置資料來源
在JBoss 的 D:/jboss420GA/docs/examples/jca 檔案夾下面,有很多不同資料庫引用的資料來源定義模板。將其中的 mysql-ds.xml 檔案Copy到你使用的伺服器下,如 D:/jboss420GA/server/default/deploy。
修改 mysql-ds.xml 檔案的內容,使之能通過JDBC正確訪問你的MySQL資料庫,如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
  <jndi-name>MySqlDS</jndi-name>
  <connection-url>jdbc:mysql://localhost:3306/lw</connection-url>
  <driver-class>com.mysql.jdbc.Driver</driver-class>
  <user-name>root</user-name>
  <password>rootpassword</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
  <metadata>
  <type-mapping>mySQL</type-mapping>
  </metadata>
</local-tx-datasource>
</datasources>

這裡,定義了一個名為MySqlDS的資料來源,其參數包括JDBC的URL,驅動類名,使用者名稱及密碼等。

2、在程式中引用資料來源:

Java code
Connection conn=null;try {  Context ctx=new InitialContext();  Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用資料來源  DataSource ds=(Datasource)datasourceRef;  conn=ds.getConnection();    ......  c.close();} catch(Exception e) {  e.printStackTrace();} finally {  if(conn!=null) {    try {      conn.close();    } catch(SQLException e) { }  }}

直接使用JDBC或者通過JNDI引用資料來源的編程代碼量相差無幾,但是現在的程式可以不用關心具體JDBC參數了。
在系統部署後,如果資料庫的相關參數變更,只需要重新設定 mysql-ds.xml 修改其中的JDBC參數,只要保證資料來源的名稱不變,那麼程式原始碼就無需修改。

由此可見,JNDI避免了程式與資料庫之間的緊耦合,使應用更加易於配置、易於部署。

JNDI的擴充:
JNDI在滿足了資料來源配置的要求的基礎上,還進一步擴充了作用:所有與系統外部的資源的引用,都可以通過JNDI定義和引用。

所以,在J2EE規範中,J2EE 中的資源並不局限於 JDBC 資料來源。引用的類型有很多,其中包括資源引用(已經討論過)、環境實體和 EJB 引用。特別是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一項關鍵角色:尋找其他應用程式組件。

EJB 的 JNDI 引用非常類似於 JDBC 資源的引用。在服務趨於轉換的環境中,這是一種很有效方法。可以對應用程式架構中所得到的所有組件進行這類組態管理,從 EJB 組件到 JMS 隊列和主題,再到簡單配置字串或其他對象,這可以降低隨時間的推移服務變更所產生的維護成本,同時還可以簡化部署,減少整合工作。外部資源”。

總結:
J2EE 規範要求所有 J2EE 容器都要提供 JNDI 規範的實現。JNDI 在 J2EE 中的角色就是“交換器” —— J2EE 組件在已耗用時間接地尋找其他組件、資源或服務的通用機制。在多數情況下,提供 JNDI 供應者的容器可以充當有限的資料存放區,這樣管理員就可以設定應用程式的執行屬性,並讓其他應用程式引用這些屬性(Java 管理擴充(Java Management Extensions,JMX)也可以用作這個目的)。JNDI 在 J2EE 應用程式中的主要角色就是提供間接層,這樣組件就可以發現所需要的資源,而不用瞭解這些間接性。

在 J2EE 中,JNDI 是把 J2EE 應用程式合在一起的粘合劑,JNDI 提供的間接定址允許跨企業交付可伸縮的、功能強大且很靈活的應用程式。這是 J2EE 的承諾,而且經過一些計劃和預先考慮,這個承諾是完全可以實現的。

 

說白了就是把資源取個名字,再根據名字來找資源。

//===============================================================================================================

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.