第一部分: 介紹Web Bean
我們現在真的臨近發布一個Web bean的社區預覽草案。該草案的目的是收集關于我們定義的組件模型,依賴管理模型和擴展上下文模型的反饋,希望讓人們對Web
Bean感興趣。我們也需要在其他的EE6 相關專家組的前面開展我們的工作,以至于它們可以考慮如何同我們已定義的一些機制盡可能的重用和集成。然而,該規范本質就
是用高級技術性語言寫的,因此在此blog是第一個以系列文章的友好形式介紹Web bean(也就是說我的翻譯是國人第一份,哈哈。)當社區預覽草案發布以后,請花時
間下載和審查它。但是請先讀此系列文章。
歷史回顧
首先介紹一下背景。Web bean 是由 JBoss 創造的,幫助填平Java EE5的裂隙。EE 5平臺對用成熟的技術包括EJB3,JTA,JCA和JPA來訪問事務資源提供了極大
支持。當然,此平臺也以支持廣泛的Web表現技術例如Java Servlets,JSP和JSF作為特征。然而,Web層和事務層彼此獨立發展,已經失去了發展一個為訪問企業事務
資源的Web應用共享的組件模型的機會。今天,Web Bean正在以JBoss,Sun,Oracle和Google作為代表,以及一些獨立成員支持下發展。組件模型受到Google Guice
和Seam的極大影響。
Java EE的一個統一組件模型
Web bean是一個同兩層技術相兼容的組件模型。Web bean同JSF和EJB3集成,允許一個EJB3會話bean作為一個JSF可管理的活動,因此同這兩個組件模型兼容。另外,Web bean提供一個會話模型和持久化上下文管理,因此解決了影響JSF和JPA的狀態管理問題和優化事務管理問題。總而言之,Web bean 使建立通過JPA訪問數據庫的Java EE Web應用程序更容易開發。當web bean為JSF和EJB3的集成提供一個輕快實現時,組件模型變得更通用。特別是,它支持沒有JSF或沒有EJB3的情況下的使用。較早提出的一個問題是某種程度上Web bean被限制在EE和EJB3環境下。專家組無異議的決定是:
- 一個Web bean不必是一個EJB;
- Web bean應該可以在容器外執行。
第一個決定只認識到不是每一個組件都需要EJB服務(例如聲明式事務,授權等)的事實。然而,Web bean將不復制此項功能,所以當需要這些服務時,Web bean需要寫成會話bean。第二個決定允許組件在應用服務器外面集成或單元測試,允許代碼級重用,例如一個批處理過程。
一些會員,尤其Bob Lee,認為我們已做的工作只是在EE平臺外面有用,并且特別是組件模型應考慮Java SE中的使用。然而,作為規范領導和根據我們JSR提議的語言,我做出決定清晰的指定目標環境是Java EE,限制我們的爭論在EE開發者需要什么。
如果將來,有來自社區和JCP的壓力開放Web bean的一部分(例如高級Guice樣式依賴注入引擎)的話,我們能夠在那時跟從在EJB3專家組有JPA建立的先例,定義EE平臺以外的行為。
Web bean 組件
精確地說一個Web bean是什么?
一個Web bean是一個包含業務邏輯的應用組件。一個Web bean可以從Java 代碼中被調用,或者通過統一了的EL調用。一個Web bean 可以訪問事務資源。兩個Web bean的依賴由Web bean容器自動管理。大多數Web Bean是有狀態和上下文的。一個Web bean的生命周期總是由容器管理。讓我們稍微回顧一下。它對于上下文意味著什么呢?因為Web bean是有狀態的,所以它同我有哪一個bean實例有關。同一個無狀態組件模型(無狀態會話bean)或者單例組件模型(例如serlets)不一樣,一個組件的不同客戶端看到組件的不同狀態。客戶端可見狀態依賴于客戶端有哪一個組件實例的引用。
然而,象一個無狀態會話bean或單例模型一樣,象JSF,但是與有狀態會話bean不一樣,客戶端不通過顯式創建和破壞它來控制實例的生命周期。作為代替,一個上下文定義:
- 實力的生命周期
- 此實例對于客戶端的可見作用域
所以在相同作用域執行的客戶端(例如其它Web bean)將看到同一實例。但是,在不同作用域的客戶端將看到不同實例。上下文模型一個很大的優勢是它允許象創建服務一樣創建有狀態組件。客戶端不需要它使用的管理的組件生命周期關注它自己,也不需要知道生命周期是什么。組件通過傳送消息交互,組件實現定義了它們自己狀態的生命周期。之所以此組件是松散耦合的,是因為:
- 它們通過定義良好的公共API交互
- 它們的生命周期完全被解耦
我們能夠用實現相同API和有一個不影響其它組件實現的不同生命周期的另一個不同組件替代組件。實際上,Web bean為在足夠人工攔截的重寫組件實現定義了一個部署時的高級功能,這將在我們將來安裝時看到。更正規的是,根據該規范:
一個Web bean組件包括:
- 組件類型
- 或這是一個bean實現類或者是解析方法
- 一系列的API類型
- 一系列(可能為空)的綁定批注類型
- 作用域
- 組件名
讓我們看一看這些條目對于組建開發者來說意味著什么。
組件類型
我們現在關于組件類型所需要知道的是一個Web bean開發者可能定義一些享元作為批注,例如允許全部組件集合在特定系統部署中有條件的安裝的@Mock, @Staging 或者 @AustralianTaxLaw。我們在后一節中的更多討論這個獨一無二的強大功能。
一個非常簡單的Web bean可能僅使用內置的組件類型@Component:
@Component public class Credentials { ... }
組件前場景類型批注識別此類作為一個Web bean到Web bean容器中。
API 類型,綁定批注和依賴注入
Web bean通常通過依賴注入獲得對于其它Web bean的引用。任何注入型參數指定一個組件必須滿足的契約。契約為:
- 一個API
- 一個 (可能為空)綁定批注集合
一個API是用戶定義類或者接口。(如果組件是一個EJB會話bean,那么此API類型是@Local接口。)一個綁定批注是一個本身用@BindingType注解的用戶定義批注。容器搜索滿足契約(實現此API和支持綁定批注)的組件,注入那個組件。例如,如果這個是注入點:
@In @CreditCard PaymentProcessor paymentProcessor;
以下組件將被注入:
@CreditCard @Component public class CreditCardPaymentProcessor implements PaymentProcessor { ... }
Web bean定一個高級但是直接解析算法,它幫助容器決定如果超過一個組件滿足一個指定契約的話將做什么。我們將在以后文章中詳細介紹。
組件作用域
作用域定義組件實例的生命周期和可見性。Web bean上下文模型是可擴展的,The scope defines the lifecycle and visibility of instances of the component. The Web Beans context model is extensible, 包含模糊作用域。然而,對于規范來說某個重要的作用域是內置的,有Web bean容器來提供。例如,任何Web 應用程序都可訪問一個會話作用域:
@SessionScoped @Component public class ShoppingCart { ... }
我們將在以后文章中詳細介紹作用域。
組件名和統一表達式語言(EL)
所有Web bean可以通過名字在統一EL表達式中使用。自定制一個Web bean名字是很容易的:
@SessionScoped @Component @Named("cart")
public class ShoppingCart { ... }
到那時我們能很容易在JSF頁使用此組件:
<h:dataTable value="#{cart.lineItems}" var="item"> .... </h:dataTable>
解析方法和web-beans.xml文件
大多數Web bean通過寫一個實現類和注解它來定義。然而,有另外兩種定義一個Web bean的方式:
- 通過一個XML部署描述文件web-beans.xml
- 通過寫一個解析方法
我們在將來的文章中介紹web-beans.xml文件。
一個解析方法是一在當前上下文中沒有實例存在時由容器調用它來獲得一個組件實例的方法。例如:
@SessionScoped @Component public class Login { User user; ... public void login() { user = ...; } @Resolves @LoggedIn User getCurrentUser() { return user; } }
一個解析方法是第一個類Web bean組件。再一次的告訴大家,后面章節繼續介紹它。
登錄
讓我們通過梳理以下前面的例子來描述一下這些觀點。我們將實現用戶登錄/注出。首先我們將定義一個組件包含用戶名和密碼,這些將在登錄過程中用到:
@Component public class Credentials { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
此組件綁定到登錄在以下JSF表格的提示符下:
<f:form> <h:panelGrid columns="2" rendered="#{!login.isLoggedIn}"> <h:outputLabel for="username">Username:</h:outputLabel> <h:inputText id="username" value="#{credentials.username}"/> <h:outputLabel for="password">Password:</h:outputLabel> <h:inputText id="password" value="#{credentials.password}"/> </h:panelGrid> <h:commandButton action="#{login.login}" rendered="#{!login.isLoggedIn}"/> <h:commandButton acion="#{login.logout}" rendered="#{login.isLoggedIn}"/> </f:form>
實際工作由一個維護關于當前進入的用戶信息和向其他組件暴露用戶實體的會話作用域組件去做:
@SessionScoped @Component public class Login { @In Credentials credentials; @In @UserDatabase EntityManager userDatabase; private User user; public void login() { List<User> results = userDatabase.createQuery( "select u from User u where u.username=:username and u.password=:password") .setParameter("username", credentials.getUserName()) .setParameter("password", credentials.getPassword()) .getResultList(); if ( !results.isEmpty() ) { user = results.get(0); } } public void logout() { user = null; } public boolean isLoggedIn() { return user!=null; } @Resolves @LoggedIn User getCurrentUser() { return user; } }
當然,@LoggedIn是一個綁定批注:
@Retention(RUNTIME) @Target({TYPE, METHOD, FIELD}) @BindingType public @interface LoggedIn {}
現在,任何其它的組件能夠很容易注入當前用戶:
@Component public class DocumentEditor { @In @Current Document document; @In @LoggedIn User currentUser; @In @DocumentDatabase EntityManager docDatabase; public void save() { document.setCreatedBy(currentUser); docDatabase.persist(document); } }
大家繼續關注我?。?/h2>
希望這篇文章為您理解Web bean組件模型提供幫助。有更多的東西需要討論,我希望您將有時間看一看此系列文章的剩余部分。
——————來自牛哄哄的Hibernate之父 Gavin King凡是有該標志的文章,都是該blog博主Caoer(草兒)原創,凡是索引、收藏
、轉載請注明來處和原文作者。非常感謝。