歡迎您光臨本站 註冊首頁

實戰角度比較EJB2和EJB3的架構異同

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply: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屬性配置:

  <persistence>

  <persistence-unit name="Ejb3Tutorial">

  <jta-data-source>java:/TestDS</jta-data-source>

  <properties>

  <property name="hibernate.hbm2ddl.auto" value="create-drop"/>

  </properties>

  </persistence-unit>

  </persistence>

  攔截器概念

  EJB3.0引入了類似AOP中的攔截器概念(注意,AOP不只等於攔截器,不能認為EJB3就是完全AOP了),JBoss使用JBossAOP來實現攔截器功能,自己定義的攔截器方法可以攔截任何一個業務方法或生命周期事件回調;攔截方法可以在bean中定義或專門的攔截器類.

  @Stateless

  @Interceptors( { NullChecker.class, ArgumentsChecker.class })

  public class StatelessSessionBean implements StatelessSession {

  // This business method is called after

  // the above two interceptor's @AroundInvoke

  // methods are invoked. Hence it is guaranteed

  // that the argument to this method is not null

  // and it starts with a letter

  public String initUpperCase(String val) {

  String first = val.substring(0, 1);

  return first.toUpperCase() val.substring(1);

  }

  }

  NullChecker和ArgumentsChecker是StatelessSessionBean兩個攔截器,在攔截器NullChecker中,必須指定的攔截方法為@AroundInvoke.

  public class NullChecker {

  @AroundInvoke

  public Object checkIfNull(InvocationContext ctx)

  throws Exception {

  Method method = ctx.getMethod();

  if (method.getName().equals("initUpperCase")) {

  String param = (String) (ctx.getParameters()[0]);

  }

  .........

  }

  // Proceed to the next interceptor

  return ctx.proceed();

  }

  }

  總結

  總之,從上面EJB2和EJB3的總結上看,EJB3.0在EJB2基礎上,引入了更多概念,最大變化就是Annotation替代了配置文件,對於一些配置文件厭惡者來說,是一個好事;但是在實戰中,在一些依賴注射不能照顧到地方,我們還必須和更加複雜的JNDI名稱打交道,這恐怕是EJB3的一個不是很完美的地方.

  關於EJB3中可測試性的優點被很多人津津樂道,將EJB脫離容器測試,雖然可以進行微觀的單元測試,但是脫離容器就是脫離特定完整的業務場景,,基於容器的(也就是基於完整的業務場景)單點跟蹤調試才是最重要的,這些必須依賴開發工具的發展,目前已經在Eclipse3.2以後版本 WPT(或JBossIDE/Lomboz)中實現,這個功能適合大部分J2EE/JavaEE程序.

  ,個人對脫離容器的測試要求並不以為然,而這個曾經是Martin Fowler定義POJO的主要內容,在過去容器概念剛剛出現時,很多人都有容器恐懼症,以為容器都是不透明的,我們的業務對象放入進去,就失去了控制,這些都是落後設計觀念導致,其實,Java語言本身提供的可跟蹤性和介入性是非常強大,目前性能跟蹤工具Profiler可以在容器運行時,跟蹤到容器中某個具體類佔據CPU多少,佔用多少內存資源,那麼一個單點調試豈是一個個所謂容器可以阻擋的?容器是Java語言的特點,ClassLoader決定了Java就是一個容器性的語言,關鍵這個容器是必須透明的.


[火星人 ] 實戰角度比較EJB2和EJB3的架構異同已經有1248次圍觀

http://coctec.com/docs/java/show-post-60357.html