top
Loading...
用EJB3.0開發企業級Bean組件初體驗
下載本文源代碼

閱讀提要 EJB 3.0規范的最終稿已經提交到JCP,如果不出意外EJB3.0將在2006年正式發布。本文作為探討EJB 3.0公共草案三系列中的第一篇,將解釋EJB 3.0和企業Bean組件的基本概念;另外,你還學習到怎樣使用JBoss和Maven來開發基于EJB 3.0的企業級bean組件。

盡管EJB 3.0規范還沒有正式發行,但是它已經在Java開發社群中引起廣泛興趣——無論是對其擁護者還是其競爭對手。所有人都承認迫切需要找到更有生產效率的軟件開發方法;如今,他們的爭論集中于在EJB 3.0。

盡管存在爭論,但是EJB 3.0規范草稿的發行和在JBoss的初步支持意味著現在正是探索這種深具影響力的技術的時候。事實上,有關在生產系統中應用EJB 3.0的報告早已浮出水面。

本文是探討EJB 3.0公共草案三系列中的第一篇。本文將向你介紹該草案中的一些專門概念并將同你一起用JBoss來實現這些具體的技術。另外,本文還涉及一些EJB 3.0和企業Bean組件的基本概念。

為此,你需要作如下準備:

·Java 2 SDK 1.5
·Maven 2.0
·JBoss應用程序服務器4.0.3
·JBoss AOP 1.3.4

一、EJB 3.0簡介

企業級應用程序是這樣一類應用程序-它們實現商業功能并包含大量的數據。這些應用程序一般不在孤立的環境中執行任務,取而代之的是,它們當中的每一段程序往往成為龐大的IT系統的一個小小的組成部分并且必須與人和另外的系統進行交互。簡而言之,開發企業級應用程序是一件相當難的事情。在幾乎所有的企業級應用程序中,為了在企業中成功地執行它們的功能,必須強調如性能,可伸縮性,并發性和安全性等一系列的問題。

作為對這些挑戰的回應,為了更容易地開發分布式的面向對象的商業系統,在1998年3月,產生了EJB規格說明書。該規格說明書和實現它的應用程序服務器已經在很大程度上實現了這個目標。但是,在過去的幾年中,EJB的不足以及隨著更簡化實現方式的出現,逐漸提出了很多問題-是否EJB能夠為企業級應用程序的生產力開發提供最好的解決方案?

EJB經常被提及的不足包括如下:

·如果想利用提供的企業服務的組件就需要使用大量的EJB API。

·它是一種要么全有要么全無的提議。就算你想要使用一種EJB服務,你也必須使用所有伴隨EJB的其它內容。

·EJB組件要求有相應的容器并且被證明是難于測試的。

·要求每個EJB實現若干內容(接口,類和描述符)。

·傳統型EJB架構很少涉及面向對象并且引入了只有狀態而沒有行為的"假對象"。

關于EJB的一些流行選擇有:

·面向方面編程(AOP)

·輕量級容器,如Spring,Pico Container和Hivemind

·對象關系映射(ORM)工具,如Hibernate,JDO和iBatis

·.NET服務組件

EJB 3.0規格說明書公共草案主要目的是使開發更為容易并使得平衡企業服務更為簡單。這些改進在某種程度上得益于以上所列舉內容的成功應用。上面工具的某些方面在EJB 3.0還留有它們的蹤跡:

·一個POJO(普通Java對象)編程模型

·一個輕量級ORM持續性框架

·依賴性注入

·元數據注解

在EJB 3.0規格說明書中,其它一些重要組成包括:

·通過異常進行配置

·消除組件接口

·消除home接口

·檢查異常使用的減少

本文假定你已經熟悉EJB 2.x并且會關注在3.0 規格說明書公共草案中所引入的新的變化。本文余下的部分將通過使用Maven和JBoss應用程序服務器來分析構建EJB 3.0應用程序的過程中的這些改進。

二、應用程序實例-一個在線音樂店

本文中所討論的技術全部展現在所附的示例應用程序中-一家在線音樂店。這家店允許用戶瀏覽和購買音樂,視頻和音頻書籍。圖1顯示出這個界面的一個屏幕快照;你可以下載本文最后有關的源代碼。


圖1.音樂店:該音樂店允許瀏覽的產品可以被添加到一個購物車上并最終實現購買。

這個示例程序展示了所有的三種類型的企業bean組件的使用,即無狀態會話beans,有狀態會話beans和消息驅動的beans。注意,在EJB 3.0規格說明書中,實體beans不再和會話beans及消息驅動的beans一樣被當作企業bean組件。代之的是,它們被認為是持續性實體(這將在我的下一篇有關EJB 3.0持續性的文章中介紹)。圖2中的類圖是一個怎樣用EJB實現該音樂店應用程序的高級展示。


圖2.音樂店設計:在這個web應用程序中,請求是用一個Struts行為來處理的-這個行為使用兩個EJB(一個無狀態,一個有狀態)來實現請求。在付款臺,顧客的購物車經由JMS傳遞到一個以一個MDB形式實現的訂單處理器。

本文相隨的實例應用程序的設計和測試工具有:JBoss應用程序服務器4.0.3和JBoss AOP 1.3.4,并且使用Maven 2.0來構建這個應用程序。所附源代碼為設置Jboss、使用Maven構建源文件和發布該應用程序均提供了指導。

三、無狀態會話Beans

與EJB 3.0規格說明書中的其它一些企業bean組件一樣,無狀態會話beans是一些實現了一個商業接口的POJO。以我們的音樂店為例:該音樂店實現在用戶瀏覽時對各種類型的音頻和視頻的查詢功能。對這個音樂店來說,實現這一功能的一個簡單的接口實現可以如下編寫:

public interface IMusicStore{
public List<Genre> listGenres();
public Genre findGenreById(int id);
public Artist findArtistById(int id);
public Product findProductById(int id);
public void checkOut(IShoppingCart cart);
}

并且下面是這種情況的一個簡單實現:

public class MusicStore implements IMusicStore{
public List<Genre> listGenres()
{ return MusicStoreDAO.getInstance().listGenres(); }
public Genre findGenreById(int id)
{ return MusicStoreDAO.getInstance().findGenreById(id); }
public Artist findArtistById(int id)
{ return MusicStoreDAO.getInstance().findArtistById(id); }
public Product findProductById(int id)
{ return MusicStoreDAO.getInstance().findProductById(id); }
public void checkOut(IShoppingCart cart)
{ ... }
}

在EJB 2.x中,如果你想要利用企業服務的優點,如聲明性事務管理或對象分布,那么你必須修改該音樂店以實現javax.ejb.SessionBean接口以及在這個接口中定義的方法;另外,還要創建本地的和遠程的bean和home接口。

在EJB 3.0中,一個類要被擴展為一個無狀態會話bean必須滿足下面兩個基本要求:

1. 實現至少一個商業接口。

2. 被用@javax.ejb.Stateless注解所注解。

一個商業接口僅是一個普通的Java接口。其中,java.io.Serializable和java.io.Externalizable接口都被自動地忽略,因為這些都是在javax.ejb包中的接口。如果只有一個接口被指定,那么它就被假定為一個本地接口。這是在規格說明書的配置中唯一的一個例子。如果這個bean有多于一個的商業接口,那么每個接口必須被顯式地定義為本地的或遠程的-通過注解這個bean類或者通過注解這些接口本身。

因此,為了把MusicStore類做成為一個無狀態的會話bean,僅需做一個改變。你必須用@Stateless注解來注解它。

@Stateless
public class MusicStore implements IMusicStore{ ...}

與EJB 3.0相關的另一個主要變化實際上是一個J2EE 1.5規格說明書中的變化-依賴性注入。J2EE 1.5規格說明書要求,J2EE應用程序客戶、企業beans和web組件都可以存取一個JNDI命名環境。該命名環境可以被存取,或者通過顯式的JNDI查找調用或通過指定容器將自動注入依賴性的注解。這意味著,現在你可以通過一個如下的簡單注解聲明一個對一個EJB或其它由容器管理的資源的依賴性:

import com.devx.musicstore.IMusicStore;
public abstract class AbstractStoreAction extends Action{
@EJB
protected IMusicStore store;
...
}

就是這樣!該容器將把接口ImusicStore的一個實現注入到該行為中,而該行為可以使用這個存儲對象,就象它是一個簡單的POJO一樣。

作者注:

在JBoss4.0.3 版本中還沒有實現針對web組件的資源注入功能,盡管這種支持即將出現(http://jira.jboss.com/jira/browse/EJBTHREE-212)。該實例代碼包含一個方面-用JBoss AOP寫成,它實現了依賴性注入。


其實背后真正進行的是,這個注解指定把一個到類型IMusicStore的EJB參考注入到store字段中。該依賴性的目標并沒有指定,因此必須由發布者來提供-就象使用傳統型的資源參考一樣。如果沒有指定依賴性名字(如上面例子中所示),那么就假定是使用類的完全限制的命名。在上面的實例中,這將是java:comp/env/com.devx.musicstore.IMusicStore。

作者注 在缺省地使用EJB參考的名字時,在有關是否使用不限制的或完全限制的類命名的問題上在EJB 3.0和J2EE 1.5規格說明書存在一些歧義。EJB 3.0公共草案指定不限制的bean類命名,而J2EE 1.5草案顯示了一個完全限制的命名示例。JBoss實現使用完全限制的命名。

作為選擇,你可以如下方式來進一步提練這種依賴性的聲明:

public abstract class AbstractStoreAction extends Action{
@EJB(
name = "ejb/musicStore",
beanName = "store1",
BusinessInterface = com.devx.musicstore.IMusicStore.class
)
protected IMusicStore store;
...
}

在此,對EJB參考的存取是通過在java:comp/env/ejb/musicStore的JNDI實現的。

四、遠程接口

為了遠程存取一個會話bean,你僅需要用如下的@Remote注解來對它進行注解即可:

@Stateless
@Remote
public class MusicStore implements IMusicStore
{ ...}

在上面的實例中,MusicStore bean僅僅實現了一個商業接口;既然這個類被指定為遠程的,所以我們假定它是遠程的。否則,如果MusicStore實現了兩個商業接口,那么你將被要求指定哪個接口是本地和哪個接口是遠程的。為此,你可以使用下面兩種方法中的一種:在類上或在接口上。改變MusicStore類來使用一個遠程接口的情況如下所示:

@Stateless
@Local( { IMusicStore.class } )
@Remote( { IMusicStoreRemote.class } )
public class MusicStore implements IMusicStore, IMusicStoreRemote
{ ...}

你可以把上面的方法與在一個接口上指定的方法進行比較:

@Remote
public interface IMusicStoreRemote
{ ...}

注意,遠程接口并沒有拋出RemoteExceptions異常。如果一個異常在協議級上出現,那么一個包裝了RemoteException的EJBException異常將由容器拋出。作為一個RuntimeException異常,EJBException不需要在遠程接口的方法簽名或在它的實現bean類中聲明。

五、有狀態的會話beans

有狀態的會話beans與無狀態的會話beans有非常相似的需求。就象無狀態的會話beans,它們必須至少有一個商業接口。然而,它們被代之用@Stateful注解所注解。因此在音樂店實例中,用戶的購物車已經被建模為一個有狀態的會話bean-既然它描述了用戶和服務器之間的有狀態的會話:

@Stateful
public class ShoppingCart implements IShoppingCart
{ ...}

如在EJB 2.x中一樣,調用相同的到一個有狀態的會話bean的參考也是由相同的EJB實例來處理的;這樣以來,把這一參考存儲到一個能夠被客戶不斷讀取的位置就顯得極為重要。在音樂店情況下,這個參考存儲在用戶的HttpSession中。

六、消息驅動的Beans

消息驅動的Beans(MDB)還要實現一個商業接口并且被注解以指示它們的bean類型。然而,在這種情況中,該商業接口并不是來自于域而是來自于一個針對所用消息系統類型的適當的聽者接口。在JMS的情況下,這是javax.jms.MessageListener。這個音樂店的訂單處理器提供一實例:

@MessageDriven
public class OrderProcessor implements MessageListener{
public void onMessage(Message message){
ObjectMessage objectMessage = (ObjectMessage) message;
try{
Order order = (Order) objectMessage.getObject();
System.out.println("Products Ordered:");
for (Product p : order.getProducts())
{ System.out.println(p.getTitle()); }
}
catch (JMSException e) { e.printStackTrace(); }
}
}

就象在EJB 2.x一樣,你可以給發布者提供關于如何配置MDB的其它信息。這個信息現在能夠通過@MessageDriven注解的activationConfig元素來提供。例如,為了定義OrderProcessor僅在JMS消息到達訂單隊列時激活,你可以對類進行如下注解:

@MessageDriven(activateConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/orders")
})
public class OrderProcessor implements MessageListener
{ ...}

但是就象在前一個版本的EJB中一樣,發布者將負責把一個MDB與一個目標或終端相關聯。

七、打包和發布

在EJB 3.0中的打包與EJB前一個版本中的打包很相似。就象在前一個版本中一樣,企業bean類必須被打包成一個.JAR文件。最大的不同在于,在EJB 3.0版本中,發布描述符成為可選的。然而,如果提供一個發布描述符的話,那么它必須存在于它通常所在位置(META-INF/ejb-jar.xml)。

該示例應用程序展示了構建一個EJB3.0 EJB-JAR并且以一個企業檔案(EAR)形式把該JAR與web應用程序(WAR)綁定到一起。該構建使用了Maven 2.0;更多信息請參考所附源碼。

作者注: 在Jboss中,EJB 3.0 jar文件的擴展名為.ejb3-這是一個JBoss慣例而并不是規格說明書中的要求。

為了發布示例應用程序,只要把結果.EAR文件復制到一個用JBoss AOP 1.3.4發布器配置的一個JBoss 4.0.3服務器的發布目錄下。所附源碼中也包含安裝指令。

八、使用一個簡化的模型

EJB 3.0公共草案展示了一種創建企業級組件的相當簡化的模型。這種新型范例的目的在于簡化開發,只是引入了較少的超出前面討論范圍的新功能。然而,EJB 3.0所提供的這種簡化應該會大大地增強團隊開發的生產能力,你將有趣地看到這種新型輕量級實現是怎樣與其它如Spring和Hibernate等輕量級框架競爭的。
作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗