JAVA項目直接觸之新手遇到的問題:JNDI 在 J2EE 中的角色

來源:互聯網
上載者:User

可以這樣理解:JNDI(Java Naming and Directory Interface)是SUN公司提供的一種標準的Java命名系統介面,JNDI提供統一的用戶端API,通過不同的訪問提供者介面JNDI SPI的實現,由管理者將JNDI API映射為特定的命名服務和目錄系統,使得Java應用程式可以和這些命名服務和目錄服務之間進行互動。相當於一個url 指定服務的位置。

 

http://www.ibm.com/developerworks/cn/java/j-jndi/index.html

掌握 J2EE 是件令人生畏的事,因為它包含的技術和縮減語在不斷地增長。Java 命名和目錄介面(Java Naming and Directory Interface,JNDI)從一開始就一直是 Java 2 平台企業版(JEE)的核心,但是 J2EE 開發新手經常用不好它。本文將消除 JNDI 在 J2EE 應用程式中所扮演角色的神秘性,並展示它如何輔助應用程式從部署細節中解脫出來。

雖然 J2EE 平台提高了普通企業開發人員的生活水平,但是這種提高是以不得不學習許多規範和技術為代價的,這些規範和技術則是 J2EE 為了成為無所不包的分散式運算平台而整合進來的。Dolly Developer 是眾多開發人員中的一員,她已經發現了一個特性,該特性有助於緩解隨企業級應用程式部署而帶來的負擔,這個特性就是 JNDI,即 Java 命名與目錄介面(Java Naming and Directory Interface)。讓我們來看看 Dolly 在沒有 JNDI 的時候是怎麼做的,以及她是如何正確地應用 JNDI 來改善其狀況的。

所有人都非常熟悉的旅程

Dolly Developer 正在編寫使用 JDBC 資料來源的 Web 應用程式。她知道自己正在使用 MySQL,所以她將一個對 MySQL JDBC 驅動程式類的引用進行了編碼,並通過使用適當的 JDBC URL 串連到其 Web 應用程式中的資料庫。她認識到資料庫連接池的重要性,所以她包含了一個串連池包,並把它配置成最多使用 64 個串連;她知道資料庫伺服器已經被設定成最多允許 128 台客戶機進行串連。

Dolly 在走向災難

在開發階段,每件事都進行得很順利。但是,在部署的時候,開始失控。Dolly 的網路系統管理員告訴她,她不能從她的案頭機訪問生產伺服器或登台伺服器(staging server),所以她不得不為每個部署階段開發不同的代碼版本。因為這種情況,她需要一個新的 JDBC URL,所以還要為測試、階段和生產進行獨立的部署。(一聽到要在每個環境中建立單獨部署,熟悉組態管理的人會戰戰兢兢的,但是既然這是種非常普遍的情況,所以他們也只好硬著頭皮上了。)

就在 Dolly 認為通過不同的 URL 建立彼此獨立的部署已經解決了自己的配置問題時,她發現她的資料庫管理員不想在生產環境中運行 MySQL 執行個體。他說,MySQL 用作開發還可以,但是對於任務關鍵型資料而言,業務標準是 DB2。現在她的構建不僅在資料庫 URL 方面有所不同,而且還需要不同的驅動程式。

事情越變越糟。她的應用程式非常有用,並且變得非常關鍵,以致於它從應用伺服器那裡得到了故障恢複的能力,並被複製到 4 個伺服器叢集。但是資料庫管理員提出了抗議,因為她的應用程式的每個執行個體都要使用 64 個串連,而資料庫伺服器總共只有 200 個可用串連 —— 全部都被 Dolly 的應用程式佔用了。更麻煩的是,DBA 已經確定 Dolly 的應用程式只需要 32 個串連,而且每天只有一個小時在使用。隨著她的應用程式規模擴大,應用程式遇到了資料庫級的爭用問題,而她的惟一選擇就是改變叢集的串連數量,而且還要做好準備,在叢集數量增長或者應用程式複製到另一個叢集時再重複一次這樣的操作。看來她已經決定了如何配置應用程式,應用程式的配置最好是留給系統管理員和資料庫管理員來做。

 



回頁首

J2EE 的角色

如果 Dolly 在開發應用程式時瞭解 J2EE 所扮演的角色,那麼她就可能避免遭遇這種困境。J2EE 規範把職責委託給多個開發角色:組件提供者(Component Provider)、應用程式組裝者(Application Assembler)、部署人員(Deployer)和系統管理員(System Administrator)。(在許多公司中,組件提供者和組件組裝者的角色是融合在一起的,部署人員和系統管理員的角色是融合在一起的。)在真正瞭解 J2EE 中的 JNDI 角色之前,掌握 J2EE 角色的作用非常重要。

組件提供者
這個角色負責建立 J2EE 組件,J2EE 組件可以是 Web 應用程式、企業級 JavaBean(EJB)組件,或者是應用程式客戶機(例如基於 Swing 的 GUI 客戶機應用程式)。組件提供者包括:HTML 設計師、文檔編程人員以及其他開發人員角色。大多數 J2EE 開發人員在組件提供者這一角色上耗費了相當多的時間。

應用程式組裝者
這個角色將多個 J2EE 模組捆綁成一個彼此結合的、可以部署的整體:企業歸檔(EAR)檔案。應用程式組裝者要選擇組件,分清它們之間的互動方式,配置它們的安全性和事務屬性,並把應用程式打包到 EAR 檔案中。許多 IDE,例如 WebSphere Studio、IDEA、JBuilder、WebLogic Workshop 和其他 IDE,都可以輔助應用程式組裝者以互動方式配置 EAR 檔案。

部署人員(Deployer)
這個角色負責部署,這意味著將 EAR 安裝到 J2EE 容器(應用伺服器)中,然後配置資源(例如資料庫連接池),把應用程式需要的資源綁定到應用伺服器中的特定資源上,並啟動應用程式。

系統管理員(System Administrator)
這個角色負責保證容器需要的資源可用於容器。

角色實戰

假設有一個公司專屬應用程式程式,該應用程式套件組合含一個 Web 應用程式,還有一個負責商務邏輯和持久性的 EJB 組件。開發這個應用程式的組件供應商可能有許多,但是在許多情況下,可以由一個人來承擔全部職責。組件可以包含資料轉送對象(一個 JAR 檔案)、EJB 介面(另一個 JAR 檔案)、EJB 實現本身(另一個 JAR 檔案),以及使用者介面組件 —— servlet、JSP、HTML 頁面和其他靜態 Web 內容。使用者介面組件被進一步打包成 Web 應用程式,其中包含 servlet 類、JSP 檔案、靜態內容,以及其他必要元件的 JAR(包括 EJB 介面)。

這聽起來好像用到的組件太多了,幾乎超出了人的想像範圍,尤其是在考慮構建一個典型的 Web 應用程式需要使用多少個 JAR 檔案的時候。但是,重要的是認識到在這裡必須小心地管理依賴性。介面和傳輸對象是 Web 應用程式和 EJB 實現可以依賴的對象,但是依賴性的方嚮應該是相同的;還要避免產生循環相依性。J2EE 組件(例如 WAR 檔案和 EJB JAR 檔案)必須在它們的部署單元之外聲明它們在資源上的依賴性。

應用程式組裝者負責把 Web 應用程式中的依賴內容包含進來,並把它們整體打包成單個公司專屬應用程式程式。工具在這裡協助很大。IDE 可以協助建立反映模組和 JAR 依賴性的項目結構,還允許您隨意指定包含或排除的模組。

部署人員負責確保部署環境中存在組件所需的資源,並將組件綁定到平台的可用資源上。例如,Web 應用程式中的外部 EJB 引用(部署描述符中的 ejb-ref)就是在此時被綁定到實際部署的 EJB 組件 —— 而且是立即綁定。

外部資源的後綁定

任何不平凡(nontrivial)的 J2EE 應用程式都需要訪問描述它期望使用環境的資訊。這意味著開發與測試組件時,為了臨時測試代碼,開發人員要承擔一些部署方面的職責。重要的是要理解:這麼做的時候,您就走出了開發人員的領域。否則,可以試著依靠 JDBC 驅動程式,或 URL、JMS 隊列名稱,或者其他具有無意識的、偶爾可能是災難性暗示的機器資源。

 



回頁首

JNDI 前來援助

Dolly 的問題的解決方案是從她的應用程式中清除所有對資料存放區的直接引用。沒有對 JDBC 驅動程式的引用,沒有伺服器名稱,沒有使用者名稱稱或口令 —— 甚至沒有資料庫池或串連管理。Dolly 需要編寫代碼來忽略將要訪問的特定外部資源,只需要知道其他人會提供使用這些外部資源所需的連結即可。這允許部署人員(任何處在這個角色的人)把資料庫連接分配給 Dolly 的應用程式。Dolly 沒有必要參與其中。(從資料庫安全性到遵守 Sarbanes-Oxley 法案,她都沒有參與進來,她這樣做也有充足的業務理由。)

許多開發人員知道:代碼和外部資源之間的緊密耦合是潛在的問題,但是在實踐中卻經常忘記角色的劃分。在小型開發工作中(指的是團隊規模或部署規模),即使忽視角色劃分也能獲得成功。(畢竟,如果應用程式只是個人的應用程式,而且您不準備依靠它,那麼把應用程式鎖定在特定的 PostgreSQL 執行個體上也挺好的。)

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

Dolly 的情況更糟了

現在我們重新來看一下 Dolly 的情況。在其簡單的 Web 應用程式中,她直接從應用程式代碼中使用了一個 JDBC 串連。參見清單 1,我們可以看出,Dolly 顯式地把 JDBC 驅動程式、資料庫 URL 以及她的使用者名稱和口令編碼到了 servlet 中:

清單 1. 典型(但是不好)的 JDBC 用法

        Connection conn=null;try {  Class.forName("com.mysql.jdbc.Driver",                true, Thread.currentThread().getContextClassLoader());  conn=DriverManager.getConnection("jdbc:mysql://dbserver?user=dolly&password=dagger");  /* use the connection here */  c.close();} catch(Exception e) {  e.printStackTrace();} finally {  if(conn!=null) {    try {      conn.close();    } catch(SQLException e) {}  }}      

 

如果不用這種方式指定配置資訊,Dolly(以及她的同伴們)使用 JNDI 來尋找 JDBC DataSource 會更好一些,如清單 2 所示:

清單 2. 使用 JNDI 得到資料來源

        Connection conn=null;try {  Context ctx=new InitialContext();  Object datasourceRef=ctx.lookup("java:comp/env/jdbc/mydatasource");  DataSource ds=(Datasource)datasourceRef;  Connection c=ds.getConnection();  /* use the connection */  c.close();} catch(Exception e) {  e.printStackTrace();} finally {  if(conn!=null) {    try {      conn.close();    } catch(SQLException e) { }  }}      

 

為了能夠得到 JDBC 串連,首先要執行一些小的部署配置,這樣我們才可以在本機群組件的 JNDI 下文中尋找 DataSource。這可能有點煩瑣,但是很容易學。不幸的是,這意味著即使是為了測試組件,開發人員也必須涉足部署人員的領地,並且還要準備配置應用伺服器。

配置 JNDI 引用

為了讓 JNDI 解析 java:comp/env/jdbc/mydatasource 引用,部署人員必須把 <resource-ref> 標籤插入 web.xml 檔案(Web 應用程式的部署描述符)。 <resource-ref> 標籤的意思就是“這個組件依賴於外部資源”。清單 3 顯示了一個樣本:

清單 3. resource-ref 入口

        <resource-ref>  <description>Dollys DataSource</description>  <res-ref-name>jdbc/mydatasource</res-ref-name>  <res-ref-type>javax.sql.DataSource</res-ref-type>  <res-auth>Container</res-auth></resource-ref>      

 

<resource-ref> 入口告訴 servlet 容器,部署人員要在 組件命名內容(component naming context) 中設定一個叫做 jdbc/mydatasource 的資源。組件命名內容由首碼 java:comp/env/ 表示,所以完整本地資源名稱是: java:comp/env/jdbc/mydatasource.

這隻定義了到外部資源的本地引用,還沒有建立引用指向的實際資源。(在 Java 語言中,類似的情況可能是: <resource-ref> 聲明了一個引用,比如 Object foo,但是沒有把 foo 設定成實際引用任何 Object。)

部署人員的工作就是建立 DataSource(或者是建立一個 Object 對象,讓 foo 指向它,在我們的 Java 語言樣本中就是這樣)。每個容器都有自己設定資料來源的機制。例如,在 JBoss 中,是利用服務來定義資料來源(請參閱 $JBOSS/server/default/deploy/hsqldb-ds.xml,把它作為樣本),它指定自己是 DataSource 的全域 JNDI 名稱(預設情況下是 DefaultDS)。在建立資源之後,第三步仍然很關鍵:把資源串連或者綁定到應用程式組件使用的本地名稱。在使用 Web 應用程式的情況下,是使用特定於供應商的部署描述符擴充來指定這個綁定,清單 4 中顯示了一個這樣的例子。(JBoss 用稱為 jboss-Web.xml 的檔案作為特定於供應商的 Web 應用程式部署描述符。)

清單 4. 用特定於供應商的部署描述符將資源綁定到 JDI 名稱

        <resource-ref>   <res-ref-name>jdbc/mydatasource</res-ref-name>   <jndi-name>java:DefaultDS</jndi-name></resource-ref>      

 

這表明應該將本地資源引用名稱( jdbc/mydatasource)映射到名為 java:DefaultDS 的全域資源。如果全域資源名稱出於某種原因發生了變化,而應用程式的代碼無需變化,那麼只需修改這個映射即可。在這裡,有兩個層級的間接定址:一個定義並命名資源( java:DefaultDS),另一個把特定於本機群組件的名稱( jdbc/mydatasource)綁定到命名的資源。(實際上,當您在 EAR 層級上映射資源時,可能還存在第三層級的間接定址。)

 



回頁首

超越資料來源

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

試想一下這種情況:當一家公司從 Order Ontology Processing Services(OOPS)購買了一個可部署的 EJB 組件來處理客戶訂單時,會發生什麼。為了便於舉例說明,我們把它叫做 ProcessOrders V1.0。ProcessOrders 1.0 有兩部分:一組介面和支援類(home 和 remote 介面,以及支援的傳輸類);實際 EJB 組件自身。選擇 OOPS 是因為它在這個領域的專業性。

該公司遵照 J2EE 規範,編寫使用 EJB 引用的 Web 應用程式。公司的部署人員把 ProcessOrders 1.0 綁定到 JNDI 樹中,將它用作 ejb/ProcessOrders/1.0,並解析 Web 應用程式的資源名稱,以指向這個全域 JNDI 名稱。目前為止,這些都是 EJB 組件非常普通的用法。但是,當我們考慮公司的開發週期與公司供應商之間的互動時,事情就變得複雜起來。在這裡,JNDI 也能協助我們。

我們假設 OOPS 發布了一個新版本,即 ProcessOrders V1.1。這個新版本有一些新功能,公司內部的一個新應用程式需要這些新功能,而且很自然地擴充了 EJB 組件的業務介面。

在這裡,公司有以下幾個選擇:可以更新所有應用程式來使用新版本,也可以編寫自己的版本,或者使用 JNDI 的引用解析,允許每個應用程式在不影響其他應用程式的情況下使用自己的 EJB 組件版本。立刻更新所有應用程式對維護來說是一場噩夢,它要求對所有組件都進行完整的迴歸測試,這通常是一項艱巨的任務,而且如果發生任何功能測試失敗的話,那麼還要進行另一輪調試。

編寫內部(in-house)組件常常是沒有必要的重複工作。如果組件是由在這個業務領域內具有專業知識的公司編寫的,那麼給定的 IT 商店不可能像專業的組件供應商那樣精通業務功能。

正如您可能已經猜到的那樣,最好的解決方案是用 JNDI 解析。EJB 的 JNDI 引用非常類似於 JDBC 資源的引用。對於每個引用,部署人員都需要把新組件按特定的名稱(比如說 ejb/ProcessOrders/1.1)綁定到全域樹中,對於需要 EJB 組件的其他每個組件,還要為組件在部署描述符中解析 EJB 引用。依賴於 V1.0 以前的應用程式不需要進行任何修改,也不需要重新測試,這縮短了實現的時間、降低了成本並減少了複雜性。在服務趨於轉換的環境中,這是一種很有效方法。可以對應用程式架構中所得到的所有組件進行這類組態管理,從 EJB 組件到 JMS 隊列和主題,再到簡單配置字串或其他對象,這可以降低隨時間的推移服務變更所產生的維護成本,同時還可以簡化部署,減少整合工作。

 



回頁首

結束語

有一個古老的電腦科學笑話:每個編程問題都可以僅僅用一個抽象層(或間接的)來解決。在 J2EE 中,JNDI 是把 J2EE 應用程式合在一起的粘合劑,但還沒有緊到無法讓人很容易地把它們分開並重新裝配。JNDI 提供的間接定址允許跨企業交付可伸縮的、功能強大且很靈活的應用程式。這是 J2EE 的承諾,而且經過一些計劃和預先考慮,這個承諾是完全可以實現的。實際上,它要比許多人想像的容易得多。

參考資料

  • 您可以參閱本文在 developerWorks 全球網站上的 英文原文。
  • 從 Sun Microsystems 公司的 The JNDI Tutorial 可以學習更多 JNDI 的知識。
  • “ Using JNDI Outside J2EE”摘自 Teach Yourself J2EE in 21 Days(Sams Publishing,2004 年),示範了如何定義正在使用的名稱服務類型,以及如何定義網路上的伺服器的主機名稱和連接埠號碼。
  • developerWorks 專欄作家 Brian Goetz 在他的文章“ J2EE or J2SE? JNDI works with both”( JavaWorld,2002 年 4 月)中,討論了如何使用 JNDI 和 J2SE 進行應用程式配置。
  • Daniel 通過解釋 J2EE 應用程式中使用的 JNDI 命名內容,展示了如何“ 走出 JNDI 迷宮”。
  • 將 JNDI 用於 WebSphere Application Server J2EE 瘦用戶端應用程式 展示了如何在 J2EE 應用程式客戶機中使用 JNDI,以便與遠程 JNDI 提供者進行通訊。
  • developerWorks Java 技術專區 可以找到數百篇有關 Java 各個方面的技術文章。
  • 請訪問 Developer Bookstore,以獲得技術書籍的完整清單,其中包括數百本 Java 相關主題 的書籍。

作者簡介

 

Kirk Pepperdine 是 JavaPerformanceTuning.com 的首席技術官,過去 15 年裡,他一直專註於對象技術和效能調整。他還是 Ant Developer's Handbook 一書(MacMillan)的合著者之一。

 

 

Joseph Ottinger 是 Indianapolis 的一位諮詢顧問,專攻 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.