本文從實戰(zhàn)角度比較EJB2和EJB3的異同,通過深入剖析揭示EJB3.0的真相,,EJB3.0真是帶來簡化,?還是一種表象上的簡化,?EJB3.0真的變得輕量了,還是披著羊皮的狼,?
EJB編程模型的簡化 首先,,EJB3簡化的一個主要表現(xiàn)是:在EJB3中,一個EJB不再象EJB2中需要兩個接口一個Bean實現(xiàn)類,,雖然我們以前使用JBuilder這樣可視化開發(fā)工具自動生成了EJB2的這三個類,,好像不覺得復(fù)雜,但是當(dāng)EJB個數(shù)增加時,,就顯得累贅了,。 簡化后的EJB3的sessionBean依靠annotations元注釋來定義SessionBean的類型,也就是說,,EJB2中的SessionBean類型區(qū)分在EJB3繼續(xù)繼承,,只不過書寫代碼的方式不同而已,例如下代碼使用@Stateless表示一個無狀態(tài)Bean,。
上述Session Bean中沒有了EJB2中ejbCreate等多余方法,,這樣TestSessionBean很象一個普通JavaBeans了。是不是簡單,?先別急,,我們需要接著看看這個TestSessionBean是如何調(diào)用? 在EJB2中,,一個EJB對象的調(diào)用需要經(jīng)過兩個步驟:JNDI尋找和工廠創(chuàng)建,,如下例:
其實上述代碼最后一句才是我們真正目的,但是為了這個目的,,必須經(jīng)過前面冗長的代碼創(chuàng)建,,而在EJB3中,為創(chuàng)建型模式的Ioc模式(或稱依賴注射)取代了home.create這樣簡單工廠創(chuàng)建模式,,以一種更加松耦合和簡潔的方式解決了對象創(chuàng)建問題,可以讓我們精力更集中在對象的使用上了,。 下面是annotations+Ioc/DI的EJB3調(diào)用代碼:
上述EJB3調(diào)用代碼中,,@EJB后面是空白,這其實使用了TestSessionLocal的缺省JNDI名稱,,一直到這里,,我們一直滿足于EJB3的簡化,但是如果研究@EJB語法后,,會發(fā)現(xiàn)其完整寫法如下:
上述完整@EJB寫法適用于同一個接口有多個實現(xiàn)子類時,,其中關(guān)鍵是 beanName的定義:beanName是被調(diào)用EJB的類名 (不帶包名,稱為unqualified name ),,或者, 如果被調(diào)用EJB有 XML descriptor定義, 它就是配置項ejb-name值(如果你使用過EJB2,就容易理解這個ejb-name了),。 @EJB還有一個屬性mappedName,,這是被調(diào)用者的JNDI名稱,一般不使用,,因為這個JNDI名稱和具體服務(wù)器有關(guān),,如果是JBoss4,那么它的缺省形式是:"EAR-FILE-BASE-NAME/BEAN--CLASS-NAME/local" (or remote). 也就是:被調(diào)用者EJB所在EAR包的名稱/Bean實現(xiàn)子類(不帶包名)/local,,如果是remote調(diào)用,,就是remote。 如果這個EJB被打包在jar包中,,那么JNDI名稱就是EJB-CLASS-NAME/local and EJB-CLASS-NAME/remote,當(dāng)然,,作為替換@RemoteBinding 和 @LocalBinding 也可定義JNDI名稱。 也就是說:JBoss的EJB3中,,如果你不使用XML配置,,直接使用annotations,那么JNDI缺省名稱沒有一個統(tǒng)一規(guī)定名稱,,有的可以直接是類名,;在JBoss中還和EJB打包的形式有關(guān),是動態(tài)變化的,。如果你以為在EJB3中不會接觸到這個變化的JNDI缺省名稱,,那你就錯了。 JBoss 4 在Servlet中不支持類似EJB調(diào)用EJB那樣的依賴注射 binding-by-injection,,因為Web容器和EJB容器是兩個不同容器,,當(dāng)然借助另外JBoss Seam則是另外一回事,因此,,在Web層調(diào)用EJB,,就必須通過JNDI綁定一個session bean,這時,,你就必須使用到那個變化不定的缺省JNDI名稱了,。 JNDI Naming Context 無論J2EE還是Java EE中,,JNDI是一個好像不起眼,,但是極其重要的概念,不理解JNDI可以說,,對J2EE或JavaEE只了解一半,。 JNDI本來是EJB2中比較復(fù)雜的一個概念,不同容器有自己的JNDI名稱,,由此EJB2引入了第三者EJB-Reference,,雖然解決了代碼中耦合JNDI名稱問題,但是又帶來了更加煩瑣的配置,這種現(xiàn)象當(dāng)然被JavaEE5.0繼續(xù)繼承了下來,,問題遠(yuǎn)非這么簡單,。 J在Java EE5.0中(包括EJB3和Web環(huán)境),當(dāng)我們需要訪問一個JNDI環(huán)境下資源時,,有兩種方式:除了傳統(tǒng)EJB2中的JNDI調(diào)用方式,;還有一種就是:使用依賴注射Ioc模式,這個依賴注射的表達方式是使用annotations,。 因此,,在EJB3中,必須好好搞清楚annotations,、依賴注射和JNDI之間的關(guān)系,,如果這個問題不弄明白,EJB3就絕非EJB2那么容易搞定,,當(dāng)然,,搞定了的結(jié)果很簡單,讓人感覺簡化輕量了,,真不知道EJB3這種簡化是不是有點象”掩耳盜鈴“,。 可以總結(jié)一句:凡是EJB2中使用配置文件定義的;EJB3一般都可以使用 annotations定義(當(dāng)然EJB3也支持配置文件定義),;凡是EJB2通過JNDI尋找的資源(調(diào)用容器中其他EJB,、調(diào)用環(huán)境變量等Resource資源等),都是可以依靠annotations+依賴注射機制完成,。 JPA替代實體Bean . 如果說EJB3與EJB2變化最大的部分,,就是持久層使用Java Persistence API 替代了EJB2的實體Bean,這樣,,我們通過Evans DDD建模得到的Domain Model類可以直接持久化保存到數(shù)據(jù)庫,,不像EJB2中還需要在Model類和實體Bean中進行一次轉(zhuǎn)換。 EJB3引入EntityManager進行需要持久實體的查詢及其新增修改,;EntityManager非常類似JDBCTemp/HibernateTemplate等持久化模板,。 JPA和JDO以及Hibernate等O/R mapping框架都是非常相似的。 雖然在JPA中,,我們都可以使用Annotation來替代配置,,實現(xiàn)很多過去需要專門配置文件才能實現(xiàn)功能,不再一定需要 每個服務(wù)器不同的cmp映射文件,,增強了移植性,但是EJB3還是需要 一個叫persistence.xml配置文件,,在這個配置中進行數(shù)據(jù)庫JNDI配置,;當(dāng)然,還有一些和具體服務(wù)器有關(guān)的配置屬性,如果使用JBoss,,JBoss的JPA底層使用Hibernate實現(xiàn),,因此在persistence.xml要進行有關(guān)Hibernate屬性配置:
攔截器概念 EJB3.0引入了類似AOP中的攔截器概念(注意,AOP不只等于攔截器,,所以不能認(rèn)為EJB3就是完全AOP了),,JBoss使用JBossAOP來實現(xiàn)攔截器功能,自己定義的攔截器方法可以攔截任何一個業(yè)務(wù)方法或生命周期事件回調(diào),;攔截方法可以在bean中定義或?qū)iT的攔截器類,。
NullChecker和ArgumentsChecker是StatelessSessionBean兩個攔截器,在攔截器NullChecker中,,必須指定的攔截方法為@AroundInvoke,。
總結(jié) 總之,從上面EJB2和EJB3的總結(jié)上看,,EJB3.0在EJB2基礎(chǔ)上,,引入了更多概念,最大變化就是Annotation替代了配置文件,,對于一些配置文件厭惡者來說,,是一個好事;但是在實戰(zhàn)中,,在一些依賴注射不能照顧到地方,,我們還必須和更加復(fù)雜的JNDI名稱打交道,這恐怕是EJB3的一個不是很完美的地方,。 關(guān)于EJB3中可測試性的優(yōu)點被很多人津津樂道,,將EJB脫離容器測試,雖然可以進行微觀的單元測試,,但是脫離容器就是脫離特定完整的業(yè)務(wù)場景,,所以,基于容器的(也就是基于完整的業(yè)務(wù)場景)單點跟蹤調(diào)試才是最重要的,,這些必須依賴開發(fā)工具的發(fā)展,,目前已經(jīng)在Eclipse3.2以后版本+WPT(或JBossIDE/Lomboz)中實現(xiàn),這個功能適合大部分J2EE/JavaEE程序,。 |
|