// Home 介面
public interface TestSessionHome extends EJBHome {
TestSession create() throws RemoteException, CreateException;
}
// Remote 介面
public interface TestSession extends EJBObject {
void xxxx() throws RemoteException;
}
// Bean 類
public class TestSessionBean implements SessionBean {
public void ejbCreate() { ... }
public void ejbActivate() { ... }
public void ejbPassivate() { ... }
public void ejbRemove() { ... }
public void setSessionContext(SessionContext ctx) { ... }
public void xxxx() { System.out.println("hello"); }
}
// 客戶端調用:JNDI + narrow + create
Context ctx = new InitialContext();
TestSessionHome home = (TestSessionHome) ctx.lookup("java:comp/env/ejb/TestSession");
TestSession bean = home.create();
bean.xxxx();
// 業務介面 (可選,預設為本地介面)
@Local
public interface TestSessionLocal {
void xxxx();
}
// Bean 實作:無需實作 SessionBean 介面,無生命週期回調
@Stateless
public class TestSessionBean implements TestSessionLocal {
public void xxxx() { System.out.println("hello"); }
}
// 客戶端調用:依賴注入 (DI)
@EJB
private TestSessionLocal testBean;
public void invoke() {
testBean.xxxx(); // 直接使用,無 JNDI、無 narrow、無 create
}
| 維度 | EJB 2.x | EJB 3.0 | 本質 |
|---|---|---|---|
| 介面定義 | 強制 Home + Remote | POJO 介面 + @Local/@Remote | 語法簡化 |
| Bean 類 | 實作 SessionBean、生命週期回調 | 純 POJO、無容器回調 | POJO 化 |
| 物件創建 | 工廠模式 Home.create() | 依賴注入 (IoC/DI) | 控制反轉 |
| 查找機制 | JNDI 字串查找 | @EJB 註解聲明式注入 | 聲明式配置 |
| 部署描述檔 | ejb-jar.xml 必須 | 註解替代,XML 可選覆蓋 | 配置簡化 |
@EJB(
name = "ejb/shopping-cart", // ejb-reference 名稱
beanName = "cart1", // 被調用 Bean 類名 (不含套件) 或 ejb-name
beanInterface = ShoppingCart.class, // 業務介面
description = "The shopping cart"
)
private Cart myCart;
beanName 與 mappedNameejb-name 值EAR名/BEAN類名/local 或 /remoteBEAN類名/local、/remoteJBoss 4 中 Web 容器與 EJB 容器分離,Servlet 無法使用 @EJB 注入。必須在 Servlet 中手工 JNDI 查找:
// Servlet 中被迫面對不穩定的預設 JNDI 名稱
Context ctx = new InitialContext();
TestSessionLocal bean = (TestSessionLocal) ctx.lookup("HelloEJB/local"); // JBoss 特定名稱
// 或使用 @RemoteBinding/@LocalBinding 固定 JNDI 名稱
結論:EJB 3.0 在 EJB 容器內簡化了調用,但跨容器 (Web→EJB) 邊界 JNDI 依賴依然存在且更脆弱 (預設名稱隨打包形式動態變化)。JBoss Seam 等框架可解決此問題,但屬於額外引入。
ejb-ref/ejb-local-ref 解耦程式碼與 JNDI 名稱,但引入繁瑣 XML 配置@EJB 註解聲明式注入,但底層 JNDI 綁定邏輯未變| 特性 | EJB 2 CMP Entity Bean | EJB 3 JPA Entity |
|---|---|---|
| 程式模型 | 抽象類 + 抽象 get/set + XML 映射 | POJO + 註解/XML 映射 |
| 領域模型 | 需 DTO 轉換 (Model → Entity Bean) | DDD 領域模型直接持久化 |
| 繼承/多型 | 不支援 | 完整支援 (SINGLE_TABLE/JOINED/TABLE_PER_CLASS) |
| 查詢 API | EJB QL (受限) | JPQL + Criteria API + Native SQL |
| 管理介面 | EntityManager (類似 HibernateTemplate) | EntityManager (標準化) |
| 移植性 | 廠商特定 CMP 映射檔 | 標準 persistence.xml + 註解 |
| 維度 | 評價 | 說明 |
|---|---|---|
| 程式碼量 | 大幅減少 | POJO + 註解替代 Home/Remote/Bean/XML,開發體驗質變 |
| 學習曲線 | 初期陡峭、長期平緩 | 需掌握註解語義、DI 原理、JPA 映射、JNDI 底層、異常轉換 |
| 除錯難度 | 注入階段錯誤難定位 | 部署時期驗證、JNDI 名稱衝突、事務邊界隱式管理 |
| 移植性 | API 層面標準化、配置層面仍有差異 | persistence.xml 廠商屬性、JNDI 命名規則、類載入隔離 |
| 運行時架構 | 本質未變 | 仍是 EJB 容器管理事務/安全/並發/生命週期,只是元資料來源變了 |
| 輕量化程度 | 開發期輕量、部署期同重 | EAR/WAR 結構、容器服務、資源佔用無本質減少 |
@RemoteBinding/@LocalBinding 固定名稱、或引入 Seam/Spring 整合EJB 3.0 的簡化是真實的開發體驗革命,但非運行時架構革命。看清這層本質,才能在享受註解便利的同時,駕馭容器服務的複雜性,不被「簡化」表象迷惑。