EJB 體系結構的歷史和目標 本文概述 Enterprise JavaBeans (EJB) 技術,旨在讓讀者快速理解基本概念。第 1 部分講述 EJB 技術的歷史和某些目標、優點和技術。為了簡潔明了,有選擇地講述EJB 技術的一些關鍵要素。請注意,雖然 EJB 組件依賴於一些基礎的 Java 服務(如 Java Transaction Service),但使用 EJB 組件及認識這些組件的好處並不需要掌握這些相關技術的知識。 Enterprise JavaBeans 技術自 1998 年 3 月問世以來很受好評。下面這段話就是一個例子: “自從兩年多以前問世以來,Enterprise JavaBeanstm 技術在平台供應商和企業的開發小組中,同樣都保持著空前的發展勢頭。這是因為 EJBtm 的服務器端組件模型簡化了中間件組件的開發,這些中間組件都是事務性的、可伸縮的和可移植的。Enterprise JavaBeans 服務器通過為中間件服務(如事務處理、安全性、數據庫連接及其他)提供自動支持,降低了開發中間件的復雜程度。”(Sun Microsystems 網站) Enterprise JavaBeans 這一名稱利用了 Java bean — 這種可移植、可重用的 Java 軟件組件的聲望。Enterprise JavaBeans 技術把 Java 組件的概念從客戶機域擴展到了服務器域:這是 Java 技術成長過程中有重大意義的一步,它使 Java 技術發展成為一種強健的、可伸縮的環境,能夠支持以任務為關鍵的企業信息系統。 服務器上的 Java 應用程序 Java 編程語言最初在 Web 開發人員中獲得好評的一個原因是,它支持稱為 applet 的可下載 Java 程序。對 Applet 的支持以 Applet 類的形式內置到了 1.0 版的 Java Development Kit (JDK) 中。按照 1.0 版的時間框架,Java 開發是以 applet 和應用程序作為中心的。基於 JDK 1.0 版的 Java 讀物都是從 applet 和應用程序的角度來描述 Java 編程的: “Java 程序由更多的類定義中的某一個組成,每個類定義均已編譯成它自已的 Java 虛擬機對象代碼的 .class 文件。這些類之一必須定義一個叫做 main() 的方法,程序就是從這個方法開始運行的。想調用一個 Java 程序,需要運行 Java 解釋器 java,並指定包含 main() 方法的類的名稱。請注意 Java applet 並不是一個應用程序 — 它是一個由已在運行的 Java 應用程序(如 Web 浏覽器或 applet 查看器)裝入並運行的 Java 類。”(見 Flanagan 所著的 Java in a Nutshell) Java 應用程序可以在服務器上運行,但是不管是在客戶機-服務器環境下,還是在基於 Web 的環境下,JDK 中都沒有提供讓 Java 應用程序專用於服務器機器的接口或包。認識到 Java 在 Web 環境下作為一種服務器語言的潛力,Sun Microsystems 編寫了 Java Servlet 規范。servlet 在許多方面與 applet 相似,它是專門為在 Web 服務器機器上運行而設計的 Java 程序: “servlet 是由容器管理的 Web 組件,可產生動態內容。servlet 是一種小型的、與平台無關的 Java 類,被編譯成體系結構中立的字節代碼,這種代碼可以動態地加載到一個 web 服務器上,並由此 web 服務器運行。servlet 通過一種由 servlet 容器實現的請求-響應模型與 Web 客戶機進行交互。這種請求-響應模型建立在超文本傳輸協議 (HTTP) 行為的基礎之上。”(見 JavaSoft 的“Java Servlet API Specification”) 在一台 Web 服務器控制下,在多台服務器上運行若干小型用戶程序,這種想法並不新鮮 — 一段時間以來,公共網關接口 (CGI) 程序(常被稱為 CGI 腳本)一直起著這種作用,並推動了 Web 的普及。但 Java servlet 可以以更高的效率和可移植性來實現這一目的,因而可望最終會取代 CGI 程序。為 servlet 提供運行時環境的軟件(通常被稱為 servlet 引擎)可以添加到現有的、本身並不支持 Java 可執行程序的 Web 服務器上。 Java servlet 的出現,為應用程序員使用 Java 來創建 Web 應用程序開辟了新的途徑。但是,僅有 servlet 還不能為真正的企業計算提供完整的模型。CGI 應用程序本身往往不是完整的應用程序,在處理接收自 Web 浏覽器上用戶的信息請求時,CGI 只是整個處理過程中的一個中間步驟。例如,CGI 應用程序的一種常見用途是訪問數據庫。將它用於這種任務時,CGI 程序提供一種方法,將用戶的數據請求連接到能滿足這種請求的企業數據庫。CGI 程序常常充當一種中間軟件,從 Web 浏覽器接收請求,決定必須調用哪些計算資源來滿足這些請求,並向浏覽器發回響應。Java servlet 與 CGI 程序一樣,最適合充當連接前端 Web 請求與後端數據資源的中間層組件。 三層體系結構 Web 編程向服務器端 Java 應用程序的演化,也帶來了體系結構的演化,使它脫離了常規的客戶機-服務器兩層模型,而向一種三層方法發展。兩層模型當時曾經具有創新意義,因為它將一些計算任務從主處理器上卸載到靈巧的客戶機。常規的基於 LAN 的數據庫應用程序就是一個例子,其中數據庫管理器服務器軟件駐留在一個專用的服務器機器上,而用戶則通過他們的工作站上的客戶機代碼來訪問數據庫。隨著客戶機-服務器模型成長到能付諸使用,就出現了對服務器可伸縮性和對客戶機代碼大小和復雜性的關注。於是提出了一種三層的體系結構,以避免在兩層模型中已察覺到的弱點,使 Web 能成為一個計算平台: “許多人...斷言,傳統的客戶機/服務器兩層體系結構不會有好的可伸縮性,因為用戶連接和數據訪問的數量無法預測,而且在一些系統管理上也存在問題。為處理兩層體系結構的限制,許多開發集體都在轉向三層體系結構。這種體系結構大致可以定義為:客戶機層上的表示層、中間的服務器和後端的某種數據庫。這種設想的目的就是緩和客戶機或數據庫服務器上的代碼膨脹,集中管理業務邏輯,更靈活地使用數據庫,而不僅是使用所存儲的過程和觸發器。”(見 Kim 的“Looking for a 3-TIEr App Builder?”) 一個三層結構模型通常被想像成有一個 Web 浏覽器作為客戶層。Web 浏覽器由於有可能成為一種真正的通用客戶機,使它從觀念上取代了兩層結構的“胖客戶機”。如果浏覽器作為 Web 應用程序體系結構的標准瘦客戶機獲得認可,那麼以前駐留在兩層模型的胖客戶機中的功能會怎麼樣呢?現在,應用程序專用的功能並不移植回服務器(例如數據庫管理器),而是有意將它駐留在一個新的中間層上。中間層支持應用程序服務器軟件,這種軟件是中間件的一種形式,它處於第一層上瘦客戶機的最小功能和第三層上服務器端業務系統的豐富功能之間。由於三層體系結構與 Web 處理模型有密切關系,所以中間層應用程序服務器常被視為 Web 服務器的一種功能擴展。現有的 Web 應用程序利用 CGI 程序,將來自 Web 浏覽器的用戶請求傳送到不基於 Web 的業務系統,並向浏覽器返回響應,就是三層模型的一種實現。這些應用程序逐漸向 servlet 技術的轉移說明三層模型正在增強。 JavaBeans 組件 JavaBeans 規范將“組件軟件”的概念引入到 Java 編程的領域。組件是自含的、可重用的軟件單元;而 JavaBeans 組件,則可以使用可視的應用程序開發工具,可視地將它們編寫到 Java 程序中。JavaBeans 規范為 Java 開發人員提供了一種“組件化”其 Java 類的方法: Bean 是一些 Java 類,可在一個可視的構建器工具中操作它們,並且可以將它們一起編寫到應用程序中。任何具有某種特性和事件接口約定的 Java 類都可以是一個 Bean。(見 JavaSoft,“Using the Beans Development Kit 1.0”) 如果軟件重用是一個好主意,那麼是否應該讓每一個 Java 類都成為 Java bean 呢?如果 Java 類滿足某些准則,它們就適於充當 bean 的角色: 在開發任何新軟件之前,都值得考慮是否用 JavaBean 的形式來開發它。如果軟件模塊要既能夠可視地操作,又能夠定制以達到某些效果,則這種軟件模塊就可能適於做成一個 JavaBean。為幫助您確定要開發的軟件是否應該是一個 JavaBean,假定它應該是用 Java 編寫的,請向您自已提出以下問題,並相應地作出決定: 是否打算讓它可重用?或者,它會是可重用的嗎? 是否希望將它與其他可重用的 Java 組件一起使用? 是否預計會在 IDE 工具中使用它? 如果上述問題的答案都是肯定的,則它應該作為 JavaBean 來開發。(見 developerWorks 的“JavaBeans Guidelines”) JavaBean 概念是為了在 Java 編程環境中支持可重用的組件,它是一種一般性的設計方法,適用於客戶機或服務器機器上運行的 Java 程序。由於對可視的構建器工具的強調,也由於許多 Java bean 都是圖形用戶界面 (GUI) 組件,所以 JavaBean 組件可能被視為一種客戶端技術。但是,並不要求 Java bean 都是可視的,並且它們也可以用於服務器環境中。 編碼為 Java bean 的 Java 類通常具有以下特征: 使用設計模式。這些模式就是方法和接口的編碼約定。 支持可視的軟件開發工具。類必須將變量(稱為屬性)、方法和事件展示出來。 可以定制。定制包括能支持缺省的屬性編輯器,或者提供單一的定制規則。定制使開發人員得以在不更改源代碼的情況下更改 bean 的行為。 支持自省 (introspection)。這指的是將屬性、方法和事件公開給其他類,可以通過設計模式或通過創建 BeanInfo 類來完成這種自省。 是持久的。這就允許在一個可視構建器中定制一個 bean,然後以其定制後的狀態加以保存。 Java 2 Platform, Enterprise Edition Sun Microsystems 發起了一項稱為 Java 2 Platform, Enterprise Edition (J2EE) 的技術創新,旨在將 Java 平台的范圍擴展到大規模服務器環境: “1997 年 4 月 12 日,Sun 宣布了一項為企業環境開發 Java 平台的創新成果。使用開放式的 Java Community Process,Sun 促進了一組標准的 Java 擴展的開發,稱為 Enterprise Java API。這些應用程序編程接口 (API) 為各種各樣的中間件的實現提供了不依賴供應商的編程接口。Enterprise Java API 的要點是 Enterprise JavaBeans API,後者為 Java 應用程序服務器定義了一個服務器端組件模型,以及一個不依賴供應商的編程接口。”(見 Thomas 的“Java 2 Platform, Enterprise Edition: Ensuring Consistency, Portability, and InterOperability”) J2EE 為 Enterprise JavaBeans 技術提供了工作環境。事實上,Sun 把若干項軟件技術都設想為這樣的構件塊,它們將使大型企業能夠把以任務為關鍵的業務系統移植到 Java 環境中,而 Enterprise JavaBeans 技術不過是這些技術之一。EJB 組件是按它們自己的規范定義的,但 EJB 技術並不是一項獨立的技術。它建立在其他 Java 技術之上,這些技術由 Sun 和其他 IT 公司聯合規定,它們一起提供了這個框架的內容,該框架就稱為 Java 2 Platform, Enterprise Edition。 J2EE 中包括以下技術: Enterprise JavaBeans (EJB) 技術 Java Interface Definition Language (IDL) Java Message Service (JMS) API Java Naming and Directory Interface (JNDI) Java Remote Method Invocation (RMI) 和 Object Serialization Java Servlet API Java Transaction API (JTA) Java Transaction Service (JTS) JavaServer Pages (JSP) 技術 JDBC 數據庫訪問 API 參與到這個企業 Java 框架中,並不意味著每項技術都依賴於所有其他技術。單獨的規范文檔指出每項技術的相關性。例如,Enterprise JavaBeans 規范 1.0 發行版就指明了在定位各個組件時與 JNDI 的相關性,以及在編程中啟動和停止事務處理時與 JTA 的相關性。 EJB 技術的設計目標 EJB 規范的第一版以初稿形式於 1997 年 12 月公布,並於 1998 年 3 月作為 1.0 版發行。規范作者為 EJB 體系結構制定了以下目標: Enterprise JavaBeans 體系結構將是標准的組件體系結構,用於以 Java 編程語言構建分布式的面向對象的商務應用程序。通過把使用不同供應商提供的工具開發出來的組件組合在一起,Enterprise JavaBeans 體系結構將有可能構建分布式的應用程序。 Enterprise JavaBeans 體系結構將使編寫應用程序變得容易:應用程序開發人員將不必了解低層次的事務和狀態管理的細節、多線程、資源共享和其他復雜的低級 API。但是,將允許專家級的程序員直接訪問低級 API。 Enterprise JavaBeans 應用程序將遵循 Java 編程語言的“一次編寫,隨處運行”的原則。EJB 組件可以只開發一次,然後在多個平台上部署,而不需要重新編譯或修改源代碼。 Enterprise JavaBeans 體系結構將處理企業應用程序生命周期中的開發、部署和運行等方面。 Enterprise JavaBeans 體系結構將定義一些約定,這些約定使多個供應商提供的工具能夠開發並部署可在運行時互操作的組件。 Enterprise JavaBeans 體系結構將與現有的服務器平台兼容。供應商將能夠擴展它們的現有產品,以支持 Enterprise JavaBeans 組件。 Enterprise JavaBeans 體系結構將與 Java 編程語言編寫的其他 API 兼容。 Enterprise JavaBeans 體系結構將提供 EJB 組件和非 Java 編程語言應用程序之間的互操作性。 Enterprise JavaBeans 體系結構將與 CORBA 兼容。 使用 EJB 技術的好處 這些設計目標會使企業和開發人員得到什麼好處呢?下面列出了可望從采用 Enterprise JavaBeans 環境獲得的好處: EJB 組件使編寫應用程序更為簡單。盡管 EJB 體系結構復雜,但應用程序開發人員一般都不必再編寫用於訪問系統服務的代碼。一種稱為 EJB 容器的系統組件使系統服務可用於 EJB 組件的任務。 服務器端商務邏輯可以移植。除了 Java 語言固有的可移植性外,EJB 體系結構還在 bean 和支持該 bean 的容器之間提供了一套標准化的應用程序編程接口。這使開發人員能夠將 bean 從一種操作環境移植到另一種操作環境,而無須重新編寫其源代碼。 可以從現有的軟件組件裝配出服務器端應用程序,這與從現有的 Java bean 可以裝配出客戶端應用程序一樣,從而使軟件能夠重用。 EJB 體系結構內置了對典型企業級系統服務的支持,包括分布式對象、事務處理、數據庫、安全和全局命名。 多家 IT 供應商都采納 EJB 體系結構,這是由於有這樣的承諾:客戶將能夠從選定的供應商那裡選購軟件組件,如 EJB 組件、容器及 EJB 服務器;也由於承諾了不同供應商的產品,只要符合 EJB 體系結構,就都是可互操作的。 用 EJB 組件構建的應用程序可以從一個服務器移植到另一個服務器,從而支持可伸縮性,這是因為在 EJB 模型中,各個軟件組件都是嚴格分離的。 EJB 體系結構能保障原有的 IT 投資,這是通過允許將現有的信息系統和資產“包裹”在這些應用程序中,而不要求客戶更換現有技術。事實上,在關系數據庫中存儲數據的企業已經有了一套已有雛形的實體 bean,正等著通過 EJB 外殼去訪問。 進一步考察 JNDI Enterprise JavaBeans 組件使用 Java Naming and Directory Interface (JNDI) 來訪問各種目錄服務。JNDI 分兩部分:應用程序編程接口 (API) 和服務供應商接口 (SPI): “JNDI 體系結構由 JNDI API 和 JNDI SPI 組成。JNDI API 允許 Java 應用程序訪問各種命名和目錄服務。JNDI SPI 則是設計來供任意一種服務的供應商(也包括目錄服務供應商)使用。這使得各種各樣的目錄服務和命名服務能夠透明地插入到使用 JNDI API 的 Java 應用程序中。(見 JavaSoft,“JNDI: Java Naming and Directory Interface”) JNDI API 並不同某種專用的命名技術或目錄技術連在一起,也不同任何供應商的目錄服務連在一起,因此它對 EJB 組件的可移植性有所貢獻。例如,客戶可以從多種不同的技術中選擇,來為其 EJB 應用程序提供目錄服務,這些技術包括: LDAP:Sun 的 LDAP 服務供應商支持 LDAP 協議的第 2 版和第 3 版。 NIS:Sun 提供一個 NIS 服務供應商(NIS 即網絡信息服務,以前稱為黃頁)。 COS 命名:Sun 的 COS 命名服務供應商提供對 CORBA 命名服務的訪問。 文件系統:Sun 提供一個服務供應商來訪問文件系統。 RMI 注冊:Sun 為 RMI 注冊提供一個服務供應商。 Novell:有幾個服務供應商可提供對目錄服務 NDS 的訪問以及 NetWare 3X 連接庫、Novell 文件系統和其他 Novell 服務(如擴展 NCP)的訪問。 雖然 JNDI 規范對供應商是中立的,但不應認為,實現 JNDI 接口的應用程序服務器一定要能訪問來自多個供應商的服務供應商代碼。 JNDI 命名體系結構的關鍵概念包括: 對象和名稱之間的綁定。 若干稱為命名上下文的綁定集。 命名系統,即若干組命名上下文。 命名空間,指一個命名系統中的所有名稱。 名稱分類為原子名稱、復合名稱和合成名稱。原子名稱是不可分割的,可以綁定到一個對象上。復合名稱是原子名稱的組合,而合成名稱則跨越多個命名系統。 命名上下文特別重要:所有的命名操作均是在上下文對象上進行的,並且名稱解析過程總是從最初的命名上下文開始。 EJB 應用程序是如何使用 JNDI 的呢?JNDI 的主要用途是檢索對 EJB 組件的引用。因為 EJB 框架是一個分布式對象框架,所以 EJB 應用程序不應當假定 EJB 組件的位置。JNDI 就是獲取對 bean 的起始引用的一種機制。當一個 bean 安裝到一個 enterprise bean 服務器上時,一個被稱為 EJB 容器的軟件組件就負責創建各個名稱-對象綁定,使所需的 Java 類文件能使用這個 bean。應用程序使用 JNDI 的查找方法來檢索對象引用,如下例所示: Context initialContext = new InitialContext( ); CartHome cartHome = javax.rmi.PortableRemoteObject.narrow( initialContext.lookup("applications/shopping_cart"), CartHome.class); 應用程序有責任知道外部名稱,應用程序就是通過這個名稱才得以引用一個 enterprise bean,並通過 JNDI 來獲取對該 bean 的引用的。 進一步考察 JTA 除 JNDI 以外,Enterprise JavaBeans 體系結構還使用 Java Transaction API (JTA)。因為事務對維護數據完整性和可靠性很重要,所以支持事務處理是 EJB 體系結構的一個基本部分。如果企業應用程序是分布式的,事務處理就會更加重要: “事務的概念是一個重要的編程范例,其目的在於簡化既要求可靠性又要求可用性的應用程序結構,特別是那些需要同時訪問共享數據的應用程序。事務的概念最早是用在商務運作的應用程序中,其中它被用於保護集中式數據庫中的數據。後來,事務的概念已擴展到分布式計算的更廣泛的環境中。今天,事務是構建可靠的分布式應用程序的關鍵,這一點已得到廣泛承認。”(見對象管理組的“Transaction Service Specification”) 有時將事務描述為具有下列特征的工作單元: 原子性 — 如果因故障而中斷,所有結果均撤銷 一致性 — 事務的結果保留不變的特性 孤立性 — 中間狀態對其他事務是不可見的 持久性 — 已完成的事務的結果是持久的 事務的終止有兩種方式:提交一個事務會使其所有的更改永久不變,而回滾 (rolling back) 一個事務則撤銷其所有的更改。 對象管理組織 (OMG) 為一種面向對象的事務服務,即對象事務服務 (OTS),創建了一種規范。OTS 是 EJB 體系結構內的事務服務的基礎。下列事務規范就是為 enterprise bean 所采用的事務模型而設: OMG 的對象事務服務 (OTS) Sun Microsystems 的 Transaction Service (JTS) Sun Microsystems 的 Java Transaction API (JTA) 開放組 (X/Open) 的 XA 接口 這種與語言無關的對象事務服務,為一個強健的分布式事務服務提供了基本概念、定義和功能。 Java Transaction Service 是 OTS 的 Java 映射,在 org.omg.CosTransactions 和 org.omg.CosTSPortability 這兩個包中定義。JTS 對事務分界和事務環境的傳播之類的服務提供支持。JTS 功能由應用程序通過 Java Transaction API 訪問。 Java Transaction API 指定事務管理器與分布式事務中涉及的其他系統組件之間的各種高級接口,這些系統組件有應用程序、應用程序服務器和資源管理器等。JTA 功能允許事務由應用程序本身、由應用程序服務器或由一個外部事務管理器來管理。JTA 接口包含在 javax.transaction 和 javax.transaction.xa 這兩個包中。 XA 接口定義了資源管理器和分布式事務環境中外部事務管理器之間的約定。外部事務管理器可以跨多個資源協調事務。XA 的 Java 映射包含在 Java Transaction API 中。