首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

實戰視角深度對比 EJB 2.x 與 EJB 3.0:簡化是真還是假?輕量是實還是虛?JNDI、註解、依賴注入、JPA 的架構級剖析

     編輯:Java 相關知識

核心論點:EJB 3.0 的簡化本質是「語法糖 + 依賴注入 + 註解驅動」的組合,而非運行時架構的根本變革

一、程式設計模型簡化:從「三類檔案」到「單一 POJO + 註解」

EJB 2.x:Home + Remote + Bean + XML 描述檔

// 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();

EJB 3.0:單一類 + 註解 + 依賴注入

// 業務介面 (可選,預設為本地介面)
@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.xEJB 3.0本質
介面定義強制 Home + RemotePOJO 介面 + @Local/@Remote語法簡化
Bean 類實作 SessionBean、生命週期回調純 POJO、無容器回調POJO 化
物件創建工廠模式 Home.create()依賴注入 (IoC/DI)控制反轉
查找機制JNDI 字串查找@EJB 註解聲明式注入聲明式配置
部署描述檔ejb-jar.xml 必須註解替代,XML 可選覆蓋配置簡化

二、@EJB 註解深度解析:JNDI 並未消失,只是被隱藏了

完整 @EJB 語法

@EJB(
    name = "ejb/shopping-cart",        // ejb-reference 名稱
    beanName = "cart1",                // 被調用 Bean 類名 (不含套件) 或 ejb-name
    beanInterface = ShoppingCart.class, // 業務介面
    description = "The shopping cart"
)
private Cart myCart;

關鍵屬性 beanName 與 mappedName

  • beanName:被調用 EJB 的簡單類名 (unqualified name),或 XML 描述檔中 ejb-name 值
  • mappedName:具體伺服器的 JNDI 全名,不可移植
    • JBoss 4.x 預設:EAR名/BEAN類名/local 或 /remote
    • 若打包為 JAR:BEAN類名/local、/remote

致命陷阱:Servlet 容器不支援 @EJB 注入

JBoss 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 等框架可解決此問題,但屬於額外引入。

三、JNDI 命名上下文:EJB 2/3 共同的核心痛點

  • JNDI 是 J2EE/Java EE 中「不起眼但極其重要」的概念
  • EJB 2 引入 ejb-ref/ejb-local-ref 解耦程式碼與 JNDI 名稱,但引入繁瑣 XML 配置
  • EJB 3 繼承此機制,新增 @EJB 註解聲明式注入,但底層 JNDI 綁定邏輯未變
  • Java EE 5 提供兩種資源訪問方式:傳統 JNDI 查找 + 註解驅動依賴注入
  • 不理解 JNDI、註解、DI 三者關係,EJB 3 反而比 EJB 2 更難除錯 (錯誤發生在注入階段,堆疊追蹤難定位)

四、JPA 替代實體 Bean:持久層的真正革命

核心變革

EntityManager 核心操作


@PersistenceContext
EntityManager em;

public User findById(Long id) { return em.find(User.class, id); }

public User create(User u) { em.persist(u); return u; }

public void delete(User u) { em.remove(em.contains(u) ? u : em.merge(u)); }

public List findByName(String name) {
    return em.createQuery("SELECT u FROM User u WHERE u.name = :name")
             .setParameter("name", name).getResultList();
}

persistence.xml 關鍵配置 (JBoss + Hibernate 實作)


<persistence>
  <persistence-unit name="my-pu">
    <jta-data-source>java:/MyDS</jta-data-source>
    <properties>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
      <property name="hibernate.show_sql" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

雖 JPA 統一了 API,但 persistence.xml 中仍需資料源 JNDI、Hibernate 方言等伺服器特定屬性,移植性非零成本。

五、總結:EJB 3.0 是「披著羊皮的狼」還是「脫胎換骨的鳳凰」?

特性EJB 2 CMP Entity BeanEJB 3 JPA Entity
程式模型抽象類 + 抽象 get/set + XML 映射POJO + 註解/XML 映射
領域模型需 DTO 轉換 (Model → Entity Bean)DDD 領域模型直接持久化
繼承/多型不支援完整支援 (SINGLE_TABLE/JOINED/TABLE_PER_CLASS)
查詢 APIEJB 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 結構、容器服務、資源佔用無本質減少

架構師決策建議

  1. 新專案優先 EJB 3 / JPA:開發效率、標準化、人才市場皆佔優
  2. 既有 EJB 2 系統穩定運行勿強遷移:ROI 低,風險高
  3. Web 層調用 EJB 必須處理 JNDI:使用 @RemoteBinding/@LocalBinding 固定名稱、或引入 Seam/Spring 整合
  4. 團隊必須建立「註解→JNDI→容器服務」心智模型:不僅會用 @EJB,更要懂其背後綁定機制
  5. 效能調優仍聚焦 Hibernate/JPA 層:N+1、延遲載入、快取、批次處理——與 EJB 版本無關

EJB 3.0 的簡化是真實的開發體驗革命,但非運行時架構革命。看清這層本質,才能在享受註解便利的同時,駕馭容器服務的複雜性,不被「簡化」表象迷惑。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved