企业级JavaBean

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

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 容器當中。

## 歷史

### 從擁抱到拋棄

由於 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 容器的作用，實現與互動。
* 提供了最早的開發者與使用者視圖。