作為輕量級容器,Spring 常被視為 EJB 的替代方案。對於許多應用場景而言,Spring 結合其在事務管理、ORM 及 JDBC 存取上的豐富支援,確實比透過 EJB 容器實現同樣功能更具優勢。然而,採用 Spring 並不意味著放棄 EJB;事實上,Spring 大幅簡化了存取與實作 EJB 組件的複雜度。透過 Spring 存取 EJB 服務,未來可在本地 EJB、遠端 EJB 或 POJO 實作之間透明切換,無需修改客戶端代碼。
傳統上調用本地或遠端無狀態 Session Bean,客戶端需執行 JNDI 查找取得 Home 介面,再呼叫 create() 取得 EJB 物件。為減少重複底層調用,常引入服務定位器與業務委派模式,但這些實作存在明顯缺陷:難以測試、仍保留 EJB API 耦合、產生大量冗餘代碼。
Spring 採用代理物件機制,在 ApplicationContext 或 BeanFactory 中配置,僅需少量代碼即可完成業務委派功能。以 LocalStatelessSessionProxyFactoryBean 為例,配置 EJB 的 JNDI 名稱與業務介面,Spring 會自動建立實作業務介面的代理,啟動時快取 Home 物件,每次調用自動處理 create() 與業務方法呼叫。Web 層控制器僅需注入業務介面,完全解除對 EJB 的依賴。
存取遠端 EJB 使用 SimpleRemoteStatelessSessionProxyFactoryBean,核心差異在於異常處理。遠端介面需拋出 RemoteException,導致切換本地/遠端時必須修改客戶端代碼。Spring 的遠端代理將 RemoteException 自動轉換為非受檢的 RemoteAccessException,客戶端無需處理受檢異常,即可在本地 EJB、遠端 EJB 甚至 POJO 之間自由切換。
Spring 提供 AbstractStatelessSessionBean、AbstractStatefulSessionBean、AbstractMessageDrivenBean 等基類,提倡將業務邏輯委派給 POJO,EJB 僅負責事務與遠端調用。範例中,MyComponentEJB 繼承 AbstractStatelessSessionBean,在 onEjbCreate() 中從 BeanFactory 取得 POJO 服務物件,業務方法直接委派呼叫。
預設使用 ContextJndiBeanFactoryLocator 依據 JNDI 環境變數建立 ApplicationContext。若多個 EJB 需共享大型上下文(如 Hibernate SessionFactory),可改用 ContextSingletonBeanFactoryLocator,在 setSessionContext() 中設定共享鍵值,實現多 EJB 共用單一上下文實例。有狀態 Session Bean 鈍化/活化時,需手動在 ejbPassivate/ejbActivate 中協載/載入 BeanFactory。
實測顯示 Spring 的代理機制效能開銷極小(雖使用反射調用),一般場景幾乎無感知。配置代理 Bean 時建議設為 lazy-init="true",避免容器啟動時因 EJB 尚未綁定導致 JNDI 查找失敗。對 AOP 深度應用者,可關注 LocalSlsbInvokerInterceptor 的實作細節。