Java™ Community Process(JCP)發布了 JSR 220 下 Enterprise JavaBeans™ (EJB) 3.0 規范的早期第二版草案,它分為三個文件:
EJB 3.0 簡化 API:定義了新型的用於代碼 Enterprise JavaBean Components 的簡化 API ,尤其是會話 bean 和消息驅動 bean。
Enterprise JavaBeans 核心契約和需求:定義了 Bean 和 EJB 容器之間的 EJB 契約。
持久性 API:定義了用於持久性的新實體 Bean 模型。
在本欄中,我覺得我應該突出 EJB 3.0 簡化 API 中的一些特性,並且評論一下能夠做一些改進的地方。因為我只提出和討論簡化 API,因此當談到 EJB 組件的時候,我將涉及到會話 bean 和消息驅動 bean,而不是實體 bean,因為在簡化 API 下,沒有包含持久性。
EJB 規范有很多目標,其中大部分目標我不會涉及到,但是簡而言之:EJB 3.0 規范包括了 J2EE™ 5.0 規范的全部目標,其目的是要簡化 J2EE 編程模型。正因如此,這部分規范與其說是一套功能性增強技術,還不如說是對原來規范的修改。然而,規范中仍然有許多新編程模型為開發者添加的技術,並且我會像查看某個特性一樣談論它們。
本文假定您已經了解了當前 EJB 規范的一些知識。
POJO 和注解
"POJO" (傳統的 Java 對象)是一個最近在很多領域不再使用的術語。它涉及到作為普通 Java 類來編寫的代碼。因為 EJB 編程可以使您擴展特定的類,可以提供幾個接口,並且可以編寫部署描述符,所以它們被視為重載的 Java 對象而不再屬於普通類。相反,開發者必須要有 J2EE 容器來運行和測試它們。我承認,不使用工具對 POJO 進行編碼使得開發很困難。在 EJB 3.0 規范中:
EJB 組件不再需要本地接口。除此之外,EJB 組件沒有必要再提供不同的接口或者擴展一些明確的 EJB 特定類別。
J2SE 5.0 注解現在是用於實現組件的一種主要的代理。通過詳細說明這些特殊的注解,開發者可以創建 EJB 組件的 POJO 類。該類將通過注解處理器得以運行,並且潛在的容器將會提供管道。
EJB 3.0 引入了業務接口的概念。以下顯示了一個實例:
public interface Stock
{
public double getQuote(String symbol);
}
Your Bean class can implement the interface:
@Stateless public class StockBean implements Stock
public double getQuote(String symbol)
{
return 100.33;
}
}
以上的@無狀態注解,意味著此類現在是一個無狀態會話 bean,將使用業務接口來調用它。早期的草案中允許存在從 Bean 類產生業務接口的可能性。無需實現特定的接口和注解就可以對無狀態會話 Bean 進行編碼。以下顯示了一個實例:
@Stateless public class StockBean
public double getQuote(String symbol)
{
return 100.33;
}
}
那麼注解處理器將會產生業務接口。在缺省情況下,所有公共的且非注入(injection)的方法都將包括在該業務接口中,除非這些方法是需要特殊注解的。舉例來說,如果使用 @BusinessMethod 至少指定一個方法,那麼只有那些使用 @BusinessMethod 指定的方法才會包括在業務接口中:
@Stateless public class StockBean
@BusinessMethod public double getQuote(String symbol)
{
return 100.33;
}
}
現在我們有一個接口,那麼我們如何指定某方法是遠程的還是本地的?當然,我們可以使用注解:
@Stateless public class StockBean
@Remote public double getQuote(String symbol)
{
return 100.33;
}
}
您可以對該業務接口或者該 Bean 類本身進行注解。當您選擇產生該業務接口的時候,使用 Bean 類上的這些注解將非常有用。在該 EJB 規范草案中沒有提及如何指定某方法是否應該被作為 Web 服務來調用;相反,這種情況委托給 JSR 181 處理,它定義了用於 Web 服務的注解。
容器服務
EJB 組件之所以流行,這是因為它對事務管理和安全性的隱性支持。EJB 3.0 規范將使用注解來應用容器服務。這裡是一個用戶如何在無狀態會話上指定事務處理的屬性的實例:
@Stateless public class StockBean
{
@TransactionAttribute(TransactionAttributeType.REQUIRESNEW)
public double getQuote(String symbol)
{
return 100.33;
}
}
該注解意味著此方法將會在新的事務中運行。閱讀此規范以獲取關於不同注解的特定語法和語義的詳細內容。容器服務同時也可以使用部署描述符得到應用,盡管當前的規范草案中並沒有標明如何來指定。然而,如果兩種方法都采用的話,部署描述符將會忽略這些注解。
特殊注解的最終形式在未來的規范草案中可以有所改動。例如,總體上與 J2EE 規范相關的確定的注解可能不再屬於 EJB。
回調
什麼是回調?在 EJB 3.0 規范發布之前,開發者必須在 Bean 類上實現回調方法,例如 ejbCreate();Bean 類必須要實現所有的方法,而不管是否使用它們。在多數情況下,這些方法的實現是空缺的。
現在通過注解來處理回調功能。這裡有兩種機制:回調方法和回調偵聽器類。下面是一個用戶如何使用回調方法編寫代碼來對回調操作做出反應的實例:
@Stateless public class StockBean implements Stock
public double getQuote(String symbol)
{
return 100.33;
}
@PostConstruct initializeCache()
{
}
}
以上代碼可以使您在創建 bean 實例後實現代碼。如果我想要使用回調偵聽器,那麼我應該創建一個回調偵聽器類:
public class MyCallbackListener
{
@PrePassivate public clearCache(Object obj)
{
Stock stock = (Stock) obj;
//perform logic
}
}
回調類並不是 Bean 類的一部分,它必須包括 java.lang.Object 參數。那麼容器就會傳送該 bean 實例。Bean 類通過使用 Bean 類級別上的特定回調注解來添加回調偵聽器:
@CallbackListener MyCallbackListener
@Stateless public class StockBean implements Stock
public double getQuote(String symbol)
{
return 100.33;
}
}
回調功能是很精密的,這是因為在您的代碼中包含它們是有條件的,而不像您實現接口時的那種情況。通過添加帶有空回調的特定抽象類同樣可以完成該操作。在規范的附錄 A 中,提到了為那些希望繼續使用 EJB 2.x 開發方式的開發著而提供的特定基類。
攔截器
EJB 規范中新添的一個細致功能就是攔截器的使用。可以進行預/後處理的能力已經從 EJB 組件中消失,這點與 servlet 過濾器對 servlet 的操作類似。開發者可以開發攔截器類,並且將其應用到 bean。以下是攔截器審查 StockBean 類的調用的實例:
public class StockRequestAudit {
@AroundInvoke
public Object auditStockOperation(InvocationContext inv) throws
Exception {
try {
Object result = inv.proceed();
Auditor.audit(inv.getMethod().getName(), inv.getParameters[0]);
return result;
} catch (Exception ex) {
Auditor.auditFailure(ex);
throw ex;
}
}
}
以上的攔截器截取了對目標 EJB 方法的調用,然後就調用 InvocationContext 上的 proceed() 方法。這樣將使得該調用可以通向被調用的實際 EJB 方法。在返回目標 EJB 方法後,它將使用 InvocationTarget 裡的元數據來獲取被調用的 EJB 組件的方法名稱和參數。那麼該攔截器就可以應用到 Bean 類:
@Stateless @Interceptors({StockRequestAudit})
public class StockBean implements Stock
public double getQuote(String symbol)
{
return 100.33;
}
}
除此之外,您可以選擇開發那些在 Bean 類內部實現的攔截器方法,同時也可以指定多個攔截器,這種情況下它們被調用的次序由在 Bean 類中定義的次序決定。
引入依賴性
使得 EJB 開發測試操作很困難的原因是 EJB 代碼依賴於數據源等因素,這種情況如同 EJB 客戶端如何調用 EJB 組件一樣。EJB 3.0 規范將引入依賴性作為一種組織機制來介紹,以此來減少這些困難。EJB 能夠通過引入代碼來定義資源引用,而不是通過使用 JNDI 查看功能。下面是一個 EJB bean 需要調用另一個 EJB 組件並且使用數據源來完成 JDBC 工作的實例:
@Stateless public class StockBean implements Stock
{
@EJB(name="MarketBean", businessInterface="Market")
Market market;
@Resource(name="StockDB, resourceType="javax.sql.DataSource")
DataSource stockDS
public double getQuote(String symbol)
{
Connection con = stockDS.getConnection();
//DO JDBC work
return market.getCurrentPrice(symbol);
}
}
依賴性的引入可以以多種方式出現,例如,通過設置屬性值的方法或者類變量。
注解還是部署描述符?
當前 EJB 3.0 規范的早期草案標記出了空缺的章節,將使用部署描述符作為注解的一種備選方案。但是注解方法會更好嗎?考慮一下"部署" 描述符的名稱,該名稱意味著它描述了與部署相關的應用程序的元素。我認為在目前的 EJB 部署描述符中的許多元素都是開發構件,而不是部署構件。那麼在部署描述符中什麼元素比較好呢?
被部署時很可能改變的元素,例如資源引用到實際資源的映射。
全面應用到 EJB 組件群的元素。注解在為特定類指明元素方面做了很多有益的工作,但是應用到多個 bean 的元素應該在外部得到描述。例如,我想將一個一般的安全角色應用到一套 EJB 組件中,而不是單獨的 EJB 組件。為該指定操作留有空間要比在每個單獨的 EJB Bean 類上定義注解更有效率。
確定的元素(留心事務劃分)應該從部署描述符中移除,這是因為它們與部署無關。允許部署器忽略例如事務語義的行為將非常具有危險。JCP 應該在以後的修改中注意這些事實。
結束語
EJB 規范中簡化 API 部分的第二個草案依然非常空洞;該規范的契約部分也已經從早期草案中消失。我期待該核心契約將在很多方面做出定義,例如注解中定義的資源引用如何向定義在應用程序服務器上的實際數據源獲取"映射",定時服務會發生什麼行為,等等。不看該核心契約,很難做出評論,因此我們將其留作以後討論。
今天的開發者應該如何去做呢?繼續使用當前的 EJB 規范,用戶的 J2EE 應用程序服務器的廠商在很長一段時間內會完全支持該規范。如果您跟隨最佳實踐,例如對自己的應用程序分層,就可以用最輕松的方式進行修改。