EJB

企業級JavaBean（Enterprise JavaBean, EJB，或稱Jakarta Enterprise Beans）是一個用來構築企業級應用的伺服器端可被管理組件。

Java企業版API（Java Enterprise Edition）中提供了對EJB的規範。EJB是一個封裝有某個應用程式之業務邏輯伺服器端組件。EJB最早於1997年由IBM提出，旋即被昇陽電腦採用並形成標準（EJB 1.0 和EJB 1.1）。其後在Java社區進程（Java Community Process）支持下陸續有一些JSR被制訂來定義新的EJB標準，分別是JSR 19 (EJB 2.0), JSR 153 (EJB 2.1) 以及最新的JSR 220 (EJB 3.0)。

EJB規範的目的在於為企業及應用開發人員實現後台業務提供一個標準方式，從而解決一些此前總是在作業過程中總是重複發生的問題。EJB以一個標準方式自動處理了諸如數據持久化，事務整合，安全對策等不同應用的共有問題，使得軟體開發人員可以專注於程序的特定需求而不再飽受那些非業務元素的困擾。

據此，EJB規範明確了一個應用伺服器應當支持的中間管理細目，包括：

- 數據持久化
- 事務處理
- 並發控制
- 基於JMS的事件驅動
- 基於JNDI的名字和空間管理
- 基於JCE和JAAS的安全管理
- 應用伺服器端的軟體組件部署
- 使用RMI-IIOP協議的遠程過程調用
- 將業務方法暴露為Web服務

此外，EJB定義文檔還指定了EJB容器和各EJB的角色定位，以及如何將EJB部署至EJB容器當中。

<image>

## 歷史

### 從擁抱到拋棄

由於IBM和Sun Microsystems等EJB提倡者力推其前景，起初一些大公司紛紛採用EJB部署他們的系統。然而隨後各種問題便接踵而至，對EJB的惡評短時間內激增。對於初學者，EJB的API顯得太過困難；對於許多程式設計師來說，書寫那些必須拋出特定異常的接口並將bean類作為抽象類實現的做法既不直觀也不正常。當然，EJB所被賦予的使命，如對象關係映射和事務管理確實有其天然複雜性，但其API之複雜還是令開發人員們覺得望而卻步，一些人開始懷疑EJB除了引入了複雜的實現手段以外似乎並未帶來什麼實際好處。

另外，實際運用中被發現，如果使用EJB來封裝業務邏輯會帶來性能上的下降。這是因為，最早的EJB規範只允許客戶端通過特定協議（如CORBA）進行遠程方法調用來調用，即使大部分實際應用根本就不需要分布式計算。直到EJB 2.0才引入了本地接口，以支持可以開發不通過網路就能直接本地調用的EJB系統。

儘管如此，EJB的廣泛普及仍然為其複雜度所制約。儘管已經有一些高質量的整合開發工具可以協助開發人員通過自動編碼解決一部分重複作業，但這並不能降低學習此項技術的難度。另一方面，「草根階層」的編程愛好者們發起了一場旨在使用 「輕量級」技術以代替複雜的EJB的運動。這些技術包括Hibernate（用於提供數據持久化和對象-關係映射）及Spring框架（用於封裝業務邏輯）。儘管它們不像EJB那樣有巨頭支持，但其在庶民間卻更加流行，並且也被一些對EJB深感失望的企業所採用。

### 重生

EJB規範起初的一個主要價值—對分布式應用進行事務管理—在隨後的實踐中被一致認為幾乎沒能派上用場。對於企業級應用來說，Spring和Hibernate等簡化框架更加實用。因此，EJB 3.0規範（JSR 220）為了迎合這個趨勢相比於其前輩進行了一次激進的大跳躍。受到Spring 影響，EJB 3.0也使用所謂的「傳統簡單Java對象（POJO）」；同時，支持依賴注入來簡化全異系統的整合與配置。Hibernate的創始人Gavin King參與了這一新版規範的制訂，並對EJB大加提倡。Hibernate的許多特性也被引入到Java持久化API當中，從而取代原來的實體bean。EJB 3.0規範大幅採用Java註解（annotation）來對代碼進行元數據修飾，從而消減了此前EJB編程的冗雜性。

相應地，EJB 3.0幾乎成為了一個全新的API，與此前的數版可謂毫無相似度可言。

## EJB種類

EJB容器可以接受三類EJB

- 會話Bean（Session Beans）
- 無狀態會話Bean（Stateless Session Beans）
- 有狀態會話Bean（Stateful Session Beans）
- 實體Bean（Entity Beans）
- 消息驅動Bean（Message Driven Beans，MDBs）

無狀態會話Bean是一類不包含狀態資訊的分布式對象，允許來自數個客戶端的並發存取。實例變量的內容在前後數次呼出中不被保留（確切地說是不保證保留）。由於不必控制與使用者間的對話資訊而減少了開銷，無狀態會話Bean不像有狀態會話Bean那樣具有資源集約性。舉例來說，一個發送郵件的EJB就可被設計為一個無狀態會話Bean。在整個會話期，使用者只向伺服器提交一個動作：發送指定郵件到指定地址。（稱為開關行為）

有狀態會話Bean是包含狀態的分布式對象，即是說，貫穿整個會話它們都要保有客戶端資訊。舉例而言，在一個網上商店進行實施結帳很可能就需要一個有狀態會話Bean，因為結帳是一個多步動作，伺服器端必須可以隨時了解到使用者已經進行到了哪一步。此外，儘管有狀態會話Bean的狀態資訊可被保持，但始終只能是由同一個使用者來訪問之。

實體Bean是含有持久化狀態的分布式對象。這個持久化狀態的管理既可以交給Bean自身（Bean-Managed Persistence，BMP），也可以託付於外部機制（Container-Managed Persistence，CMP）。

消息驅動Bean是支持異步行為的分布式對象。它們並不對請求進行當即響應。比方說，某網站使用者點擊「請通知我更新資訊」按鈕，將會觸發某個MDB將這名使用者加入到資料庫的希望獲得更新資訊使用者列表中。這個動作就是一個異步的消息驅動過程，因為使用者不必等待當時會返回某個結果。MDB的消息源來自Java消息服務（JMS）提供的消息隊列或消息主題。自EJB 2.0規範起，JMS被加入進來以允許在容器內部實施事件驅動處理。與其他EJB不同，MDB不存在一個使用者視圖（如需要使用者引用的遠程接口），使用者也不能通過資源定位獲得一個MDB實例。MDB只在後台監聽消息源並實施自動處理。

除了上述以外，目前還有一些EJB處於設想階段，如JSR 86提出了用於在Java EE應用中整合多媒體對象的媒體Bean（Enterprise Media Beans）。

## EJB實行

EJB部署於應用伺服器端的EJB容器中。規範給定了EJB與EJB容器之間，以及使用者代碼與EJB/EJB容器之間的交互方式。對於Java EE API，javax.ejb包定義了EJB類，javax.ejb.spi包定義了EJB容器應當實現的各個接口。

在EJB 2.1和以前的版本中，每個EJB都由一個類和兩個接口組成。EJB容器負責創建這個類的實例，接口則供客戶端調用。

兩個接口分別被稱為Home接口和組件接口，負責提供各個EJB遠程方法聲明。這些EJB遠程方法可分成兩組：

- 類方法：由Home接口提供。與特定實例無關，僅負責一些公共內容，比如創建一個新的EJB實例（create方法），或尋找一個已經存在的EJB實例（find方法）等等。
- 接口方法：由組件接口提供的針對特定實例的業務方法。

EJB容器將為這些接口提供對應的實現類以充當客戶遠程代理，當客戶端調用這個生成的代理類的某個方法時，代理類內部會將此調用的方法和參數封裝成一個消息發送給伺服器。伺服器收到消息後在轉發給真實的EJB實例，後者負責執行真正的業務邏輯。

### 遠程通信

EJB規範要求EJB容器能夠支持基於RMI-IIOP的EJB訪問。EJB既可被任何CORBA應用訪問，也能提供Web服務。

### 事務

EJB容器必須支持符合ACID（原子性／一致性／獨立性／持久性）特性的容器級事務管理，以及bean內部事務管理。容器級事務需在部署描述符中（EJB應用的配置文件）進行聲明。

### 事件

EJB使用JMS向客戶對象發送消息，客戶則可以異步地接受這些消息。MDB則接受來自客戶端的消息。

### 命名和目錄服務

EJB客戶端使用JNDI或CORBA名字服務定位Home接口實現 對象。通過此Home接口，使用者還可以尋找，創建或刪除實體對象。

### 安全

EJB容器對客戶端的訪問權限負責。

### 部署EJB

EJB規範還定義了一個跨平台的統一部署機制。部署描述符中定義了關於EJB應用的一切相關內容。文件名通常為ejb-jar.xml。

部署描述符是一個XML文檔，負責為該EJB應用中的每一個EJB定義入口。部署描述符的主要內容包括：

- Home接口名
- Bean的Java類名
- Home接口的Java接口名
- 組件接口的Java接口名
- 持久化存儲（針對實體Bean）
- 安全策略和角色分配

通常EJB容器提供者還定義了一些額外的XML或其他格式描述文件來強化其容器的功能。他們還同時提供這些描述文件的解讀工具類和對Home接口的自動實現類生成。

EJB3.0起開始廣泛使用Java注釋替代傳統的部署描述符ejb-jar.xml。但後者仍然有效。

## 版本變化

### EJB 3.0

2006年5月2日發布，JSR 220定義。

- 全面採用Java注釋代替部署描述符。（後者仍可使用，並且具有更高優先級）
- 把2.X版的EntityBean改為由JPA支持。

### EJB 2.1

2003年11月24日發布，JSR 153定義。

- Web服務：可將無狀態會話bean暴露為Web服務；EJB可通過引用訪問Web服務。
- EJB定時器服務：提供一種新的基於定時器的事件驅動方式。可供消息驅動bean作為消息源使用。
- 增加了消息目的地。
- 進一步豐富了EJB查詢語言，支持ORDER BY, AVG, MIN, MAX, SUM, COUNT和MOD。
- 使用XML schema代替DTD以定義部署描述符。

### EJB 2.0

2001年8月22日發布，JSR 19 定義。

- 制定了構建面向對象商務應用的標準組建結構。
- 支持構築使用不同開發工具所開發之組件的聯合應用部署。
- 在多線程，連接池，事務管理等方面對使用者透明化。
- 使符合「一次寫成，多次運行」的Java思想。
- 關注企業級應用生命期間的開發，部署，運行等動作。
- 定義了不同開發工具所需遵守的契約，以便其產品能夠在運行期交互。
- 支持與現行系統兼容，開發者可以擴展現有產品以使之支持EJB。
- 與其他Java API兼容。
- 支持EJB與Java2平台企業版或者其他非Java應用程式之間的互操作性。
- 支持與CORBA兼容的RMI-IIOP。

### EJB 1.1

1999年12月17日發布。

- 開始採用XML部署描述符，默認的JNDI上下文以及可支持IIOP的RMI。
- 安全機制由角色（Role）驅動，而非方法。
- 支持實體類，且必須在應用中實現。

### EJB 1.0

1998年3月24日發布。

- 定義了EJB和EJB容器的作用，實現與互動。
- 提供了最早的開發者與使用者視圖。

## 外部連結

- JSR 19: Enterprise JavaBeans 2.0
- JSR 153: Enterprise JavaBeans 2.1
- JSR 220: Enterprise JavaBeans 3.0
- JSR 318: Enterprise JavaBeans 3.1/Interceptors 1.2
- JSR 345: Enterprise JavaBeans 3.2
- Jakarta Enterprise Beans 3.2 | The Eclipse Foundation
- Jakarta Enterprise Beans 4.0 | The Eclipse Foundation