本文從實戰角度比較EJB2和EJB3的異同,通過深入剖析揭示EJB3.0的真相,EJB3.0真是帶來簡化?還是一種表象上的簡化?EJB3.0真的變得輕量了,還是披著羊皮的狼?
EJB編程模型的簡化
首先,EJB3簡化的一個主要表現是:在EJB3中,一個EJB不再象EJB2中需要兩個接口一個Bean實現類,雖然我們以前使用JBuilder這樣可視化開發工具自動生成了EJB2的這三個類,好像不覺得復雜,但是當EJB個數增加時,就顯得累贅了。
簡化後的EJB3的sessionBean依靠annotations元注釋來定義SessionBean的類型,也就是說,EJB2中的SessionBean類型區分在EJB3繼續繼承,只不過書寫代碼的方式不同而已,例如下代碼使用@Stateless表示一個無狀態Bean.
package example;
@Stateless
public class TestSessionBean implements TestSessionLocal{
public void xxxx(){
System.out.println("hello");
}
}
上述Session Bean中沒有了EJB2中ejbCreate等多余方法,這樣TestSessionBean很象一個普通JavaBeans了。是不是簡單?先別急,我們需要接著看看這個TestSessionBean是如何調用?
在EJB2中,一個EJB對象的調用需要經過兩個步驟:JNDI尋找和工廠創建,如下例:
Context ctx = new InitialContext();
TestSessionLocalHome home = (TestSessionLocalHome)ctx.lookup("Java:comp/env/ejb/TestSession");
TestSessionLocal bean = home.create();
bean.xxxx();//真正目的 對象使用
其實上述代碼最後一句才是我們真正目的,但是為了這個目的,必須經過前面冗長的代碼創建,而在EJB3中,為創建型模式的Ioc模式(或稱依賴注射)取代了home.create這樣簡單工廠創建模式,以一種更加松耦合和簡潔的方式解決了對象創建問題,可以讓我們精力更集中在對象的使用上了。
下面是annotations+Ioc/DI的EJB3調用代碼:
@EJB //注意這裡後面是空白
private TestSessionLocal testbean; //使用接口聲明
public void invoke(){
testbean.xxxx(); //直接使用
}
上述EJB3調用代碼中,@EJB後面是空白,這其實使用了TestSessionLocal的缺省JNDI名稱,一直到這裡,我們一直滿足於EJB3的簡化,但是如果研究@EJB語法後,會發現其完整寫法如下:
@EJB(
name = “ejb/shopping-cart”,//被調用者Cart實現類的ejb-reference名稱
beanName = “cart1”, //被調用者的名稱 beanName
beanInterface = ShoppingCart.class, //接口名稱
description = “The shopping cart for this application”
)
private Cart myCart;
上述完整@EJB寫法適用於同一個接口有多個實現子類時,其中關鍵是 beanName的定義:beanName是被調用EJB的類名 (不帶包名,稱為unqualifIEd name ),或者, 如果被調用EJB有 XML descriptor定義, 它就是配置項ejb-name值(如果你使用過EJB2,就容易理解這個ejb-name了)。
@EJB還有一個屬性mappedName,這是被調用者的JNDI名稱,一般不使用,因為這個JNDI名稱和具體服務器有關,如果是JBoss4,那麼它的缺省形式是:"EAR-FILE-BASE-NAME/BEAN――CLASS-NAME/local" (or remote)。 也就是:被調用者EJB所在EAR包的名稱/Bean實現子類(不帶包名)/local,如果是remote調用,就是remote. 如果這個EJB被打包在jar包中,那麼JNDI名稱就是EJB-CLASS-NAME/local and EJB-CLASS-NAME/remote,當然,作為替換@RemoteBinding 和 @LocalBinding 也可定義JNDI名稱。
也就是說:JBoss的EJB3中,如果你不使用XML配置,直接使用annotations,那麼JNDI缺省名稱沒有一個統一規定名稱,有的可以直接是類名;在JBoss中還和EJB打包的形式有關,是動態變化的。如果你以為在EJB3中不會接觸到這個變化的JNDI缺省名稱,那你就錯了。
JBoss 4 在Servlet中不支持類似EJB調用EJB那樣的依賴注射 binding-by-injection,因為Web容器和EJB容器是兩個不同容器,當然借助另外JBoss Seam則是另外一回事,因此,在Web層調用EJB,就必須通過JNDI綁定一個session bean,這時,你就必須使用到那個變化不定的缺省JNDI名稱了。
JNDI Naming Context
無論J2EE還是Java EE中,JNDI是一個好像不起眼,但是極其重要的概念,不理解JNDI可以說,對J2EE或JavaEE只了解一半。
JNDI本來是EJB2中比較復雜的一個概念,不同容器有自己的JNDI名稱,由此EJB2引入了第三者EJB-Reference,雖然解決了代碼中耦合JNDI名稱問題,但是又帶來了更加煩瑣的配置,這種現象當然被JavaEE5.0繼續繼承了下來,問題遠非這麼簡單。
J在Java EE5.0中(包括EJB3和Web環境),當我們需要訪問一個JNDI環境下資源時,有兩種方式:除了傳統EJB2中的JNDI調用方式;還有一種就是:使用依賴注射Ioc模式,這個依賴注射的表達方式是使用annotations.
因此,在EJB3中,必須好好搞清楚annotations、依賴注射和JNDI之間的關系,如果這個問題不弄明白,EJB3就絕非EJB2那麼容易搞定,當然,搞定了的結果很簡單,讓人感覺簡化輕量了,真不知道EJB3這種簡化是不是有點象“掩耳盜鈴”。
可以總結一句:凡是EJB2中使用配置文件定義的;EJB3一般都可以使用 annotations定義(當然EJB3也支持配置文件定義);凡是EJB2通過JNDI尋找的資源(調用容器中其他EJB、調用環境變量等Resource資源等),都是可以依靠annotations+依賴注射機制完成。
JPA替代實體Bean
。如果說EJB3與EJB2變化最大的部分,就是持久層使用Java Persistence API 替代了EJB2的實體Bean,這樣,我們通過Evans DDD建模得到的Domain Model類可以直接持久化保存到數據庫,不像EJB2中還需要在Model類和實體Bean中進行一次轉換。
EJB3引入EntityManager進行需要持久實體的查詢及其新增修改;EntityManager非常類似JDBCTemp/HibernateTemplate等持久化模板。
JPA和JDO以及Hibernate等O/R mapping框架都是非常相似的。
雖然在JPA中,我們都可以使用Annotation來替代配置,實現很多過去需要專門配置文件才能實現功能,不再一定需要 每個服務器不同的cmp映射文件,增強了移植性,但是EJB3還是需要 一個叫persistence.XML配置文件,在這個配置中進行數據庫JNDI配置;當然,還有一些和具體服務器有關的配置屬性,如果使用JBoss,JBoss的JPA底層使用Hibernate實現,因此在persistence.XML要進行有關Hibernate屬性配置:
Java:/TestDS