日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Seam - 無縫集成 JSF,第 1 部分: 為 JSF 量身定做的應(yīng)用程序框架

 jadar 2010-09-25

2007 年 5 月 21 日

JavaServer Faces (JSF) 是用于 Java? Web 應(yīng)用程序的第一個標(biāo)準(zhǔn)化的用戶界面框架。而 Seam 是一個擴(kuò)展 JSF 的強(qiáng)大的應(yīng)用程序框架。在這個由三部分組成的新系列中的第一篇文章中,發(fā)現(xiàn)這兩種框架之間的互補(bǔ)性。Dan Allen 介紹了 Seam 對 JSF 生命周期的增強(qiáng),包括上下文狀態(tài)管理、 RESTful URL、Ajax remoting、適當(dāng)?shù)漠惓L幚砗图s定優(yōu)于配置。

JSF 正開始憑借其 Java Web 標(biāo)準(zhǔn)的地位主導(dǎo) Java Web 應(yīng)用程序市場。隨著更多的開發(fā)人員受托使用 JSF 作為基礎(chǔ)來架構(gòu)應(yīng)用程序,他們發(fā)現(xiàn) JSF 的核心規(guī)范中清楚地說明: JSF 不是為成為一個完整的 Web 應(yīng)用程序框架而設(shè)計的。相反,它提供一個健壯的、事件驅(qū)動的 API 和 UI 組件庫,用于構(gòu)建更復(fù)雜的應(yīng)用程序框架。

我在尋找用于彌補(bǔ) JSF 的組件驅(qū)動架構(gòu)的擴(kuò)展時,發(fā)現(xiàn) Shale 和 Struts 2 都有不足之處。我排除了 Struts 2,因為它將 JSF 看作是面向更大范圍的設(shè)計。而 Shale 似乎更靠近一些,它基本上是基于 JSF,但是 對此我持保留意見。相反,JBoss Seam 是一個全面的應(yīng)用程序框架,它構(gòu)建在 JSF 的基礎(chǔ)上,但是并沒有損害它的核心目標(biāo)。

這個由三部分組成的系列將介紹 Seam 應(yīng)用程序框架,演示它的優(yōu)點,并希望使您相信它與 JSF 是開發(fā) Java 企業(yè)應(yīng)用程序的極好的組合。 在閱讀本系列之前,如果您想下載 Seam,那么請閱讀 參考資料 一節(jié)。

尋找 Seam

關(guān)于 Shale

Shale 有一段并不美妙的歷史。它因 Struts-JSF 集成包而生,但是后來受到 Struts 開發(fā)人員的冷落。如今,它成了專用于 JSF 的一個頂級 Apache 項目。

我認(rèn)為 Shale 的問題在于將自身定位為一組松散耦合的服務(wù),這等于將集成的負(fù)擔(dān)壓在了開發(fā)人員肩上。Shale 的 Java 5 語言增強(qiáng)是不錯,但是它們都被包裝在一個擴(kuò)展包中。視圖控制器通過命名約定與一個模板耦合,這也帶來很多限制。 Shale 應(yīng)用程序控制器和對話框管理器都是大型增件,它們似乎為標(biāo)準(zhǔn) JSF 生命周期減輕了很多負(fù)擔(dān)。

Seam 可以提供 Shale 中所有的特性,而且將這些特性放在一個良好集成的、緊密耦合的包中。

剛剛閱讀到關(guān)于 JBoss Seam 的文章(見 參考資料) 的第一頁,我就知道 Seam 正是我要找的項目。Seam 的開發(fā)人員,尤其是 Gavin King,在經(jīng)過足夠多的、實際的開發(fā)之后,知道一個 Web 應(yīng)用程序框架必須從一開始就攻破難題,包括上下文狀態(tài)管理、RESTful 和用戶友好的 URL、Ajax remoting、適當(dāng)?shù)漠惓L幚砗图s定優(yōu)于配置。令 Java 開發(fā)人員欣喜的是,Seam 可以滿足所有這些需求,甚至可以滿足更多需求。如果您正使用 JSF,并且還沒聽說過 Seam,那么我強(qiáng)烈建議您看看 Seam 的參考文檔(見 參考資料)。Seam 附帶的手冊就是最好的資料!

盡管 Seam 顯然非常適合作為 JSF 的補(bǔ)充,但是在激烈的競爭環(huán)境中,它遭到了一定程度的輕視。當(dāng)今市場中充斥著各種各樣的 Web 應(yīng)用程序框架 —— 包括 Shale 和 Struts 2,新來者往往不受重視,Seam 還沒有在主流行列站穩(wěn)腳跟。 Seam 沒有很快流行的另一個原因是關(guān)于這種框架的某些流言使 Java 開發(fā)人員沒能認(rèn)識到它的直接優(yōu)點。

我要粉碎的一個流言是:Seam 只有和 EJB 3 一起使用時才有用,或者說在使用 Seam 開發(fā)應(yīng)用程序時需要一個 EJB3 容器。實際上,Seam 的文檔清楚地駁斥了這種誤解:"Seam 并不要求組件是 EJB,甚至在沒有兼容 EJB 3.0 的容器時也能使用。" 如果說只有在使用 EJB 3 的同時才能使用 Seam,那么無異于說只有在使用 Hibernate 的同時才能使用 Spring。雖然這兩對都有很強(qiáng)的互補(bǔ)性,但是每一對的兩者之間都不是相互依賴的。

JBoss Seam 與 JSR 299

JBoss Seam 是一種開源應(yīng)用程序框架,其目的是提高 JSF 與業(yè)務(wù)組件(例如 EJB 3 和 Spring bean)之間的集成。Seam 能夠跨 Web 環(huán)境中的不同上下文管理組件,并且?guī)缀醣苊饬嗽陂_發(fā) JSF 應(yīng)用程序時進(jìn)行 XML 配置。該項目是 Hibernate 的創(chuàng)立者 Gavin King 的杰作,目前還在 JBoss 實驗室中。

最近,JBoss 向 JCP 提交了一個建議,要求標(biāo)準(zhǔn)化 Seam 背后的概念。該建議被接受為 JSR 299, Web Beans。這個規(guī)范的目的是統(tǒng)一 JSF 管理的 bean 組件模型和 EJB3 組件模型,形成一種明顯簡化的用于基于 Web 的應(yīng)用程序編程模型。

對 EJB3 的考慮

正如我將要解釋的那樣,Seam 通過一些有價值的 hook 和組件管理進(jìn)程 擴(kuò)展默認(rèn) JSF 生命周期。還可以完全獨(dú)立于 EJB3 使用 Seam。但是要記住,和 EJB3 一樣,Seam 依賴于 JDK 5 注釋元數(shù)據(jù)進(jìn)行組件聲明,因此使用 Seam 時,還需要同時使用兼容 Java 5 的 JVM。圖 1 顯示了一個 Seam POJO 實現(xiàn)的應(yīng)用程序堆棧:


圖 1. 一個 Seam POJO 應(yīng)用程序堆棧
Seam pojo 架構(gòu)圖

實際上,即使完全不引用 EJB 3 jar 或描述符文件,也可以使用 Seam 的很多功能。當(dāng)和 POJO 一起使用 Seam 時,該框架保留對組件實例化的完全控制,并且不要求任何專門的配置。Seam 負(fù)責(zé)大多數(shù) Java 5 注釋處理,而不需要依賴于 EJB 3 中的任何機(jī)制。的確 依賴于 EJB3 容器的一組有限的注釋則是專用于那個環(huán)境的。在某些情況下,將 Seam 集成到一個沒有 EJB 3 耦合的 IT 投資中可以獲得更好的成本效益。如何使用 Seam 視個人偏好而定。





回頁首


配置并使用

如今有那么多種 Java 框架,每天只有有限的那么多小時,顯然,如果 Seam 難于集成的話,它就無立足之地。幸運(yùn)的是,將 Seam 添加到項目中很簡單。因為 JSF 生命周期仍然是 Seam 應(yīng)用程序的中心部分,所以不需要經(jīng)歷一個再訓(xùn)練時期。只需添加 4 個 jar 文件,注冊一個 servlet 監(jiān)聽器和一個 JSF phase 監(jiān)聽器,最后再加上一個空白的 Java 屬性文件。完成這些設(shè)置后,就可以一次性地將本地 JSF 應(yīng)用程序轉(zhuǎn)移到 Seam 管理的 bean 上。

要開始使用 Seam,首先需要將所需的 jar 文件添加到項目中。如果您當(dāng)前不是使用 Hibernate,或者還沒有升級到最新的版本,那么在設(shè)置時需要執(zhí)行一個額外的步驟。這里需要包含來自 Hibernate 3.2 distribution 的 jar,以及它的眾多的依賴項。Seam 還使用 Hibernate 注釋用于數(shù)據(jù)驗證,所以除了主 Hibernate jar 之外,還必須包括那個擴(kuò)展 jar。需要的 Seam 發(fā)行版中的庫有 jboss-seam.jar 和 jboss-seam-ui.jar,以及兩個支持庫:Javassist(用于 Java 的加載時反射系統(tǒng))和 Java Persistence API。圖 2 中的項目樹說明了一個 Seam 項目中的 jar 集合。該圖中顯示的大多數(shù)附加庫支持 JSF 的 MyFaces 實現(xiàn)。


圖 2. Seam 項目中的 jar 庫
JAR 文件清單

配置 Seam

接下來的步驟是在 web.xml 文件中安裝 servlet 監(jiān)聽器類。該監(jiān)聽器在部署應(yīng)用程序時初始化 Seam。


清單 1. Seam servlet 監(jiān)聽器配置
                        <listener>
                        <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
                        </listener>
                        

接下來,將 JSF phase 監(jiān)聽器添加到 faces-config.xml 文件中,如清單 2 所示。該監(jiān)聽器將 Seam 集成到標(biāo)準(zhǔn) JSF 生命周期中。(圖 3 大致描繪了集成到這個生命周期中的 Seam 增強(qiáng)。)


清單 2. Seam phase 監(jiān)聽器配置
                        <lifecycle>
                        <phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
                        </lifecycle>
                        

最后,將一個空的 seam.properties 文件放在 類路徑的根下,以便指示 Seam 進(jìn)行加載,如清單 3 所示。這個空白文件被用作一個 JVM 類加載器優(yōu)化,使 Seam 在類路徑下更小的區(qū)域內(nèi)搜索組件,從而大大減少加載時間。


清單 3. Seam 屬性文件
                        # The mere presence of this file triggers Seam to load
                        # It can also be used to tune parameters on configurable Seam components
                        

當(dāng)然,在這種最小設(shè)置中,Seam 的很多特性是不可用的。以上說明只是為了演示 Seam 很少涉足入門級使用。例如,Seam 包括一個 servlet 過濾器,該過濾器擴(kuò)展 JSF 生命周期以外的 Seam 特性。 servlet 過濾器的用法包括與非 JSF 請求集成,通過重定向傳播 conversation,以及管理文件上傳。請參閱 參考資料,看看 Seam 參考文檔,其中討論了用于控制附加功能的配置文件 —— 特別是 EJB3 集成。





回頁首


與 Seam 關(guān)聯(lián)

與典型的 JSF 配置過程相比,使用 Seam 開發(fā)受管 bean 非常容易。為了將 bean 暴露到 JSF 生命周期中,只需在類定義的上面添加一個簡單的注釋 @Name。然后,Seam 會負(fù)責(zé)控制組件的可見性和生命周期。最妙的是,不需要在 faces-config.xml 文件中定義這個 bean。

清單 4 顯示了 @Name 注釋以及 @DataModel@DataModelSelection、@In、@Out@Factory。這些注釋使變量能夠在視圖模板和 Seam 組件之間雙向流動。

在 Seam 用語中,這個動作被稱作雙射(bijection,即 bidirectional injection 的簡稱)。當(dāng)注出(outject)屬性數(shù)據(jù)時,視圖可以通過名稱找到它。在 postback 或者組件初始化時,數(shù)據(jù)被注入(inject)到 一個組件中。后者是著名的控制反轉(zhuǎn)(inversion of control,IOC)模式的一種實現(xiàn),可用于連接委托對象。傳統(tǒng) IOC 與 Seam 的雙射之間的主要不同點在于,雙射使長期作用域中的組件可以引用短期作用域中的組件??梢赃M(jìn)行這種連接是因為 Seam 在調(diào)用組件時(而不是啟動容器時)解析依賴項。雙射是有狀態(tài)組件開發(fā)的基礎(chǔ)。

顯然,清單 4 中的 POJO bean 只是簡單地演示了 Seam 的用法。隨著本系列討論的繼續(xù),我將探索另外的方法來實現(xiàn) Seam。


清單 4. 一個典型的 Seam POJO bean
                        @Name("addressManager")
                        public class AddressManagerBean {
                        @DataModel
                        private List<Address> addresses;
                        @DataModelSelection
                        @Out( required = false )
                        private Address selectedAddress;
                        @Factory( value = "addresses" )
                        public void loadAddress() {
                        // logic to load addresses into this.addresses
                        }
                        public String showDetail() {
                        // no work needs to be done to prepare the selected address
                        return "/address.jspx";
                        }
                        public String list() {
                        return "/addresses.jspx";
                        }
                        }

Spring 的注入

為了使用由一個已有的 Spring 容器管理的服務(wù)層對象中的投資,需要將所有處理相關(guān)業(yè)務(wù)邏輯的 Spring bean 注入到 Seam 組件中。首先需要確保已經(jīng)配置了 Spring-JSF 集成,它由 Spring 框架附帶的一個定制變量解析器進(jìn)行處理(見 參考資料)。有了這座橋梁,Spring 與 Seam 的集成就很簡單,只需使用 @In Java 5 注釋和一個值綁定表達(dá)式,以表明 Seam 組件的哪些屬性應(yīng)該接收一個 Spring bean 的注入,如清單 5 所示。(將來版本的 Seam 將包括用于 Spring 的一個定制的名稱空間,以滿足值綁定表達(dá)式的需要。)


清單 5. 注入一個 Spring bean
                        @Name("addressManager")
                        public class AddressManagerBean {
                        @In("#{addressService}")
                        private AddressService addressService;
                        }
                        

這個例子設(shè)置支持使用以輕量級容器(這里就是 Spring)配置的無狀態(tài)服務(wù)和數(shù)據(jù)訪問(DAO)層。因為不需要 EJB3,所以部署的目標(biāo)可以是任何基本的 servlet 容器。

現(xiàn)在,您對 Seam-JSF 實現(xiàn)有了一個初步的印象,接下來我將更深入地探討我在使用 JSF 時遇到的挑戰(zhàn),以及 Seam 如何緩解這些挑戰(zhàn)。

再談 JSF

為了充分理解 Seam 為 JSF 帶來了什么,就需要理解 JSF 與其他流行的基于 Web 的編程方法有何不同。JSF 是實現(xiàn)傳統(tǒng)的 Model-View-Controller (MVC) 架構(gòu)的一種 Web 框架。不同之處在于,它采用該模式的一種特別豐富的實現(xiàn)。與 Model 2 或者 Struts、WebWork 和 Spring MVC 之類的框架中使用的 “push-MVC” 方法相比,JSF 中的 MVC 實現(xiàn)更接近于傳統(tǒng)的 GUI 應(yīng)用程序。前面那些框架被歸類為基于動作的(action-based),而 JSF 則屬于基于組件模型 的新的框架家族中的一員。

如果將基于動作的框架想象為使用 “push” 模型,而將組件框架想象為使用 “pull” 模型,那么這種區(qū)別就很容易理解了。組件框架中的控制器不是預(yù)先處理頁面請求(在基于動作的框架中控制器就是這么做的),而是在請求生命周期中作出讓步, 在視圖中調(diào)用數(shù)據(jù)提供方法。此外,頁面上的元素,即組件被綁定到事件,這些事件可以觸發(fā)服務(wù)器端對象(激活后)的方法調(diào)用,從而導(dǎo)致重新顯示相同的視圖, 或者轉(zhuǎn)換到另一個頁面。因此,組件框架也被歸類為事件驅(qū)動的。組件框架抽象出用于事件通信的底層請求-響應(yīng)協(xié)議。

事件驅(qū)動方法的優(yōu)點是可以減少單個方法在呈現(xiàn)視圖時需要預(yù)先做的工作。在組件框架中,UI 事件或解析的值綁定表達(dá)式直接導(dǎo)致方法調(diào)用。

一個應(yīng)用程序即使只達(dá)到中度成熟,它通常也需要在任何給定頁面上使用很多不相關(guān)的活動。如果將對所有這些信息的管理全部放 入一個動作或者一個動作鏈中,那么勢必給維護(hù)帶來極大的困擾。因此,開發(fā)人員常常發(fā)現(xiàn)他們的代碼偏離了面向?qū)ο竽P偷能壍?,反而陷入了過程編程模型的泥 潭。相反,組件框架將這種工作隔離出來,更自然地加強(qiáng)了對象的角色和責(zé)任。





回頁首


Seam 與 JSF

對于 JSF 和組件框架的基礎(chǔ)已經(jīng)介紹得差不多了。實際上 —— 很多 Java 開發(fā)人員最近發(fā)現(xiàn) —— 轉(zhuǎn)移到 JSF 并非總是一帆風(fēng)順。采用組件模型會帶來一些全新的問題,首要的一個問題是您通常需要試著使應(yīng)用程序符合基于動作的 Web。很多時候,JSF 需要具有像基于動作的框架那樣的行為,但是在標(biāo)準(zhǔn) JSF 中這是不可行的,至少不為每個請求使用 phase 監(jiān)聽器就不行。

JSF 的其他主要缺點還包括對 HTTP 會話的依賴過重(尤其是在一序列的頁面之間傳播數(shù)據(jù)時),簡陋的異常處理,缺少書簽支持,以及太多的 XML 配置。 通過與 JSF 自然地集成,同時加入 JSF 規(guī)范委員會放棄的或者忽略掉的新功能,Seam 解決了很多這樣的問題。Seam 的框架鼓勵使用緊湊的、易讀的、可重用的代碼,并且避免了所有為解決上述問題而常常加入的 “粘連(glue)” 邏輯。圖 3 涵蓋了 JSF 生命周期中用于簡化應(yīng)用程序代碼的大多數(shù) Seam 擴(kuò)展點:


圖 3. Seam 生命周期增強(qiáng)
Seam 生命周期圖

讓我們來考慮其中一些增強(qiáng),因為它們適用于 JSF 開發(fā)中一些常見的挑戰(zhàn)。

并不復(fù)雜的配置

Seam 演示了 Java 5 注釋的一個非常實用的用法。Seam 的部署掃描程序檢查所有包含 seam.properties 文件的歸檔文件,并為所有標(biāo)有 @Name 注釋的類創(chuàng)建一個 Seam 組件。由于 Java 語言缺乏用于在代碼級添加元數(shù)據(jù)的一種公共語法,因此需要設(shè)計很多 XML 配置。當(dāng) Java 5 規(guī)范中加入注釋后,就獲得了一個更好的解決方案。由于大多數(shù) backing bean 是為了在特定應(yīng)用程序中使用而開發(fā)的,因此沒有理由將這些 bean 的配置 “抽象” 到類本身以外的任何文件中。附帶的好處是,您可以少處理一個文件。Seam 提供了一組完整的注釋來幫助將 bean 集成到 JSF 生命周期中。清單 4 顯示了其中一些。

頁面動作和 RESTful URL

在不使用組件框架的情況下,另一個必須解決的熟悉的問題是預(yù)先處理每個請求,就像在基于動作的框架中那樣。受此影響的用例 是 RESTful URL、書簽支持、通過 URL 模式獲得的安全性以及頁面流驗證等。這也是學(xué)習(xí)使用 JSF 的開發(fā)人員容易感到困惑的主要原因之一。有些 JSF 供應(yīng)商通過用開發(fā)人員工具提供 onPageLoad 功能來繞過這個問題(見 參考資料),但這不是核心規(guī)范的一部分。

當(dāng)用戶直接從書簽(比如)請求一個商品詳細(xì)信息屏幕時,通常會發(fā)生什么事情呢?由于 JSF 控制器采取被動方式,當(dāng)頁面開始呈現(xiàn)時,即使明顯沒有目標(biāo)數(shù)據(jù),也不能將用戶重新帶到邏輯流的開始處。相反,這種情況下只能顯示一個空頁面,其中只有一些 空值或其他可能存在的假信號。

首先,您可能會本能地想要在頁面的主 backing bean 上實現(xiàn)一個 “prerender” 方法。然而,在組件框架中,backing bean 與頁面之間的關(guān)系并不一定都是一對一的。每個頁面可能依賴于多個 backing bean,每個那樣的 bean 也可能在多個不同的頁面上使用。必須用某種方式將一個視圖 ID(例如 /user/detail.jspx)與一個或多個方法關(guān)聯(lián)起來,當(dāng)選擇呈現(xiàn)相應(yīng)的視圖模板時就調(diào)用這個(些)方法。您可以使用 phase-listener 方法,但是這仍然需要定制的邏輯來確定對于當(dāng)前視圖和階段是否應(yīng)該執(zhí)行該功能。這種解決方案不但會導(dǎo)致很多冗余邏輯,而且會將視圖 ID(很可能是應(yīng)用程序中最不確定的部分)硬編碼到編譯后的 Java 代碼中。

頁面動作來幫忙

Seam 的頁面動作可以幫助您預(yù)先攔截呈現(xiàn)的假信號。頁面動作是使用方法綁定指定的,方法綁定在進(jìn)入頁面時、Render Response 階段之前執(zhí)行。對于 /WEB-INF/pages.xml 配置文件中一個給定的視圖 ID,可以配置任意數(shù)量的方法綁定。(或者,可以通過將它們放在視圖模板鄰近的一個文件中,復(fù)制它的名稱,但是將文件擴(kuò)展名換為 *.page.xml,從而分解每個頁面的定義)。對于頁面動作,XML 是有必要的,因為視圖 ID 非常容易變化。就像 JSF 通過 Apply Request Values 階段的值綁定將 post 數(shù)據(jù)映射到模型對象一樣, Seam 可以通過執(zhí)行頁面動作之前的值綁定將任意請求參數(shù)映射到模型對象。這些請求參數(shù)注入的配置嵌套在頁面動作 XML 聲明中。如果頁面動作方法調(diào)用返回一個非空字符串值,則 Seam 將其當(dāng)作一個導(dǎo)航事件。因此,不必遷移到一個完整的基于動作的框架中,仍然可以比得上最特別的特性。Seam 包括很多內(nèi)置的頁面動作,它們通??鐟?yīng)用程序使用。其中包括用于驗證 conversation 是否建立的一個動作;可以啟動、嵌套和結(jié)束 conversation 的動作;處理預(yù)期異常的動作;以及確保適當(dāng)?shù)膽{證的動作。

頁面動作是啟用對 JSF 的書簽支持的關(guān)鍵。Seam 的創(chuàng)立者允許在進(jìn)入頁面時請求參數(shù) actionMethod 觸發(fā)一個方法調(diào)用,從而利用了這一特性。更妙的是,您不需要做任何額外的工作就能為書簽創(chuàng)建鏈接。 Seam 提供了兩個組件標(biāo)記:s:links:button,用以處理細(xì)節(jié)。這兩個標(biāo)記分別對應(yīng)于 JSF 中的 h:commandLinkh:commandButton。不同之處在于,Seam 組件標(biāo)記組裝的鏈接使用一個 HTTP GET 操作發(fā)出請求,而不是使用 JSF 的 HTTP POST 表單提交模型表示。因此,Seam 創(chuàng)建的鏈接對書簽更 “友好”,對于開發(fā)人員來說更方便。

您可能還注意到,當(dāng)使用頁面動作時,地址欄中的 URL 對應(yīng)于正在顯示的頁面,而不總是背后的一個頁面。(后一種情況之所以會發(fā)生,是因為 JSF 將表單配置為 post 回生成它們的 URL。地址欄沒有更新,以反映執(zhí)行動作后的新視圖,因為 JSF 通過一個服務(wù)器端重定向使之前進(jìn)。)如果您想演示頁面動作的靈活性,那么可以使用它們來創(chuàng)建 RESTful URL(例如 /faces/product/show/10)。為此,將頁面動作方法映射到視圖 ID“/product/show/*”,其中 /faces 前綴是 JSF servlet 映射部分。然后,該頁面動作方法利用請求 URL,以判斷數(shù)據(jù)類型和數(shù)據(jù)標(biāo)識符,加載數(shù)據(jù),然后導(dǎo)航到適當(dāng)?shù)哪0?。這個例子很明顯地演示了 JSF 請求 URL 與視圖模板之間并不一定存在一對一的關(guān)系。

工廠組件

JSF 最大的一個失敗是沒有在用戶觸發(fā)的動作或動作監(jiān)聽器方法以外的其他地方提供可靠的機(jī)會來為視圖準(zhǔn)備數(shù)據(jù)。將邏輯放在一個動作方法中并不能保證該邏輯在視圖呈現(xiàn)之前得到執(zhí)行,因為頁面視圖并不總是在用戶觸發(fā)的事件之后。

例如,當(dāng)一個 JSF 應(yīng)用程序的 URL 第一次被請求時,會發(fā)生什么情況?如果需要在該頁面上顯示從服務(wù)層獲得的一組數(shù)據(jù),那么在 JSF 生命周期中始終沒有好的機(jī)會來取數(shù)據(jù)。您可能會認(rèn)為,可以將邏輯放在映射到視圖中值綁定表達(dá)式的 backing bean 的 getter 方法中。但是,每當(dāng) JSF 解析那個表達(dá)式時,就會觸發(fā)另一個調(diào)用,新的調(diào)用又會訪問服務(wù)層。即使頁面上只有少數(shù)幾個組件,getter 方法也可能被推后數(shù)次執(zhí)行。顯然,就性能而言這不是最優(yōu)的。即使通過使用受管 bean 上的私有屬性維護(hù)狀態(tài),每當(dāng)面對那樣的情況時,仍然必須增加額外的管道。一種解決方案是使用 Seam 的頁面動作。但是由于這種任務(wù)是如此常見,Seam 提供了一個更加容易的解決方案。

Seam 引入了工廠數(shù)據(jù)提供者(factory data provider)的概念,工廠數(shù)據(jù)提供者由 @Factory Java 5 注釋指定。雖然有兩種方法配置工廠,但是最終結(jié)果是同樣的數(shù)據(jù)只需在第一次被請求時準(zhǔn)備一次。 Seam 確保隨后對相同數(shù)據(jù)的請求將直接返回之前創(chuàng)建的結(jié)果集,而不必再一次觸發(fā)對查找方法的調(diào)用。通過與 conversation 相結(jié)合,工廠數(shù)據(jù)提供者成為實現(xiàn)數(shù)據(jù)短期緩存的非常強(qiáng)大的特性,否則,取這些數(shù)據(jù)的代價可能較高。在 JSF 不負(fù)責(zé)減少它解析一個值綁定表達(dá)式的次數(shù)的情況下,Seam 的工廠特性常常變得非常方便。

有狀態(tài) conversation

關(guān)于 JSF 很容易引起困惑的一個地方是它的狀態(tài)管理功能。JSF 規(guī)范解釋了在接收一個動作之后頁面是如何 “恢復(fù)(restored)” 的,在此期間時間事件要進(jìn)行排隊,選擇要注冊。仔細(xì)研究規(guī)范中的用詞可以發(fā)現(xiàn),雖然在 postback 上恢復(fù)了組件樹,但是那些組件使用的 backing bean 數(shù)據(jù)并沒有被恢復(fù)。組件只是以字符串文字的形式存儲值綁定(使用 #{value} 語法的 EL 表達(dá)式),只在運(yùn)行時解析底層數(shù)據(jù)。這意味著如果一個值是短期作用域存儲的,例如頁面或請求作用域,那么當(dāng) JSF 生命周期到達(dá) Restore View 階段時,這個值將消失。

不將值綁定數(shù)據(jù)存儲在組件樹中的一個最值得注意的不利方面是虛幻事件效果(見 參考資料),這是由 UIData 家族中的臨時父組件導(dǎo)致的。如果一個值綁定表達(dá)式引用的模型數(shù)據(jù)不再可用,或者在組件樹被恢復(fù)之前發(fā)生了更改,那么組件樹的一些部分將被撤銷。如果在這些 被撤銷的分支中,有一個組件中觸發(fā)了一個事件,那么它將不能被發(fā)現(xiàn),而且這種事件丟失情況是難于覺察的。(只是隊列開發(fā)人員可能會驚呼 “為什么我的動作沒有被調(diào)用?”)

雖然丟失的事件看上去像是異常狀況,但并不會導(dǎo)致 JSF 生命周期中出現(xiàn)紅色標(biāo)志。因為這些組件依賴底層數(shù)據(jù),以保持穩(wěn)定和適當(dāng)?shù)乇换謴?fù),所以 JSF 難于知道丟失了什么。

不幸的是,JSF 規(guī)范天真地引導(dǎo)開發(fā)人員將大多數(shù) backing bean 放入 conversation 作用域中 —— 甚至可以在 “方便的” 作用域內(nèi)調(diào)用它。然后,服務(wù)器管理員則必須處理由此導(dǎo)致的 “內(nèi)存溢出” 錯誤,集群環(huán)境中的服務(wù)器相似性,以及服務(wù)器重啟時的串行化異常。MyFaces Tomahawk 項目通過 t:saveState 標(biāo)記的形式提供了對虛幻事件的一個解決方案。MyFaces 標(biāo)記允許將數(shù)據(jù)(包括整個 backing bean)存儲為組件樹的一部分,而僅僅是值綁定。然而,這種解決方案有些簡陋,很像使用隱藏的表單字段在請求之間傳遞值。 它還造成視圖與控制器之間緊密耦合。Seam 的創(chuàng)立者意識到,Java Servlet 規(guī)范中三個內(nèi)置的上下文(請求、會話和應(yīng)用程序)不足以構(gòu)成數(shù)據(jù)驅(qū)動的 Web 應(yīng)用程序的全部作用域。在 Seam 中,他們引入了 conversation 作用域,這是一個有狀態(tài)作用域,由頁面流的起止點界定。

Seam 的 conversation 作用域

“Seam 強(qiáng)調(diào)使用有狀態(tài)組件。” Seam 參考文檔中的這句話體現(xiàn)了 Seam 的核心思想。很長一段時間內(nèi),關(guān)于 Web 應(yīng)用程序的看法是,它們是無狀態(tài)的 —— 這種思想一定程度上要?dú)w因于 HTTP 協(xié)議的無狀態(tài)性質(zhì)。大多數(shù)框架為了迎合這一點,在結(jié)束頁面呈現(xiàn)之前提供 one-shot-processing。這種方法導(dǎo)致很大的阻力,因為任何大的應(yīng)用程序都需要長時間運(yùn)行的 conversation 來滿足某些用例。需要有狀態(tài)上下文的應(yīng)用程序的例子有很多,例如存儲檢查過程、產(chǎn)品定制、多頁表單向?qū)Ш秃芏嗥渌诰€形交互的應(yīng)用程序。雖然其中有些例 子可以通過使用 URL 參數(shù)(aka RESTful URL)和隱藏字段在頁面之間遷移數(shù)據(jù),但是這樣做對于開發(fā)人員來說有些繁雜。而且,如今這種做法已經(jīng)過時了。因為大多數(shù) Web 框架仍然在無狀態(tài)模型下操作,所以您常常發(fā)現(xiàn)自己走出了這種框架,而 “開辟” 出定制解決方案。

JSF 大量依賴于 HTTP 會話,試圖引入有狀態(tài)上下文。實際上,當(dāng)和會話作用域的 backing bean 一起使用時,JSF 組件的行為要好得多。如果不小心設(shè)計,過度使用 HTTP 會話會導(dǎo)致嚴(yán)重的內(nèi)存泄漏、性能瓶頸和安全問題。此外,在多標(biāo)簽瀏覽器環(huán)境中,使用 HTTP 會話可能導(dǎo)致非常奇怪的行為,破壞用戶神圣的 Back 按鈕。值得注意的是,JSF 只是與您互作讓步:它是一個有狀態(tài) UI,處理保存和恢復(fù)組件樹的所有細(xì)節(jié),但是它在保存和恢復(fù)數(shù)據(jù)方面沒有提供幫助。因此,JSF 帶來有狀態(tài) UI,而您則帶來有狀態(tài)數(shù)據(jù)。不幸的是,需要由您來負(fù)責(zé)確保它們是相符的。

在 Seam 之前,使用有狀態(tài)數(shù)據(jù)的惟一方便的方式是依賴于 HTTP 會話。Seam 糾正了這個問題,它通過建立一個全新的 conversation 作用域, 完善了 JSF 的狀態(tài)管理。隨著將 Seam 添加到 JSF 生命周期中,conversation 上下文與一個瀏覽器窗口(或標(biāo)簽頁)聯(lián)系在一起,這個瀏覽器窗口(或標(biāo)簽頁)由隨每個請求提交的一個標(biāo)志來標(biāo)識。conversation 作用域使用 HTTP 會話的一個單獨(dú)的區(qū)段在頁面之間遷移數(shù)據(jù)。記住,Seam 使用 HTTP 會話用于 conversation 持久性這一點是完全透明的。 Seam 并不是不負(fù)責(zé)任地將組件推卸到 HTTP 會話中,使其茫然地呆在那里。相反,Seam 小心地管理那個區(qū)段的會話數(shù)據(jù)的生命周期,并且當(dāng) conversation 終止時,自動清除它們。Seam 聰明地使用雙射來允許以一種新的說明性方式使數(shù)據(jù)流入和流出一個 “Web conversation” 的每個頁面。 Seam 的 conversation 作用域同時還克服了 HTTP 會話的限制,幫助開發(fā)人員放棄使用 HTTP 會話。

異常處理

Seam 的創(chuàng)立者曾說過:“在異常處理方面,JSF 非常有限”。這一點顯然毫無爭議。 JSF 規(guī)范完全忽視異常管理,將責(zé)任完全放在 servlet 容器上。允許 servlet 容器處理異常的問題在于,這嚴(yán)重限制了錯誤頁面上顯示的內(nèi)容,并且禁止了事務(wù)回滾。由于錯誤頁面是在請求分發(fā)器轉(zhuǎn)發(fā)之后顯示的,FacesContext 不再在作用域中,因此這時執(zhí)行業(yè)務(wù)邏輯就太遲了。您的最后一線希望是使用 Servlet API,并抓住 javax.servlet.error.* 請求屬性,以搜索能表明出錯處的信息。

這一點上,Seam 再次扮演救世主,它提供了優(yōu)雅的、說明性方式的異常處理。異常管理可以通過注釋指定,或者在配置文件中指定??梢詫⒆⑨?@HttpError、@Redirect@ApplicationException 放在異常類的上面,表明當(dāng)異常被拋出時應(yīng)該采取的動作。對于那些不能修改的異常類,可以使用 XML 配置選項。Seam 中的異常管理器負(fù)責(zé)發(fā)送 HTTP 狀態(tài)碼、執(zhí)行重定向、促使頁面呈現(xiàn)、終止 conversation 和定制出現(xiàn)異常時的用戶消息。由于在開始呈現(xiàn)響應(yīng)之后,JSF 不能改變動作的過程,一些固有的限制規(guī)定了何時才能處理這些異常。通過適當(dāng)使用其他 Seam 特性,例如頁面動作,可以確保大多數(shù)異常情況在呈現(xiàn)響應(yīng)之前得到解決。

Ajax remoting

由于最后發(fā)行的 JSF 規(guī)范幾乎與 Ajax 重合,JSF 框架在異步 JavaScript 和局部頁面呈現(xiàn)(partial page rendering)方面幫助不大。在某些時候,甚至這兩種類型的編程甚至不一致。 最終的解決辦法是建議使用 JSF PhaseListener 或組件 Renderer 來處理局部頁面更新。即使如此,這一點仍然很明顯:JSF 使得 Ajax 比過去更難于采用。有些項目,例如 ICEfaces,甚至用一個更好的、專為頁面-服務(wù)器通信設(shè)計的技術(shù)(即 direct-to-DOM rendering)來替代 JSF 生命周期。

Seam 為 JavaScript remoting(常常記在 Ajax 名下的一種技術(shù))提供了一種獨(dú)特的方式,該方式與 Direct Web Remoting (DWR) 庫的方式大致相似。Seam 通過允許 JavaScript 直接調(diào)用服務(wù)器組件上的方法,將客戶機(jī)與服務(wù)器連在一起。Seam remoting 比 DWR 更強(qiáng)大,因為它可以訪問豐富的上下文組件模型,而不僅僅是一個孤立的端點。這種交互構(gòu)建在 JSF 的事件驅(qū)動設(shè)計的基礎(chǔ)上,所以它可以更嚴(yán)格地遵從 Swing 范例。最妙的是,提供這個功能的同時并沒有增加開發(fā)方面的負(fù)擔(dān)。只需在組件的方法上加一個簡單的注釋 @WebRemote,JavaScript 就可以訪問該方法。當(dāng)服務(wù)器組件的狀態(tài)被修改之后,Ajax4JSF 組件庫就可以處理局部呈現(xiàn)。簡言之:Seam remoting 庫使 JSF 可以實現(xiàn)它的創(chuàng)立者一向期望的交互設(shè)計。





回頁首


結(jié)束語

根據(jù)您目前在 無縫集成 JSF 系列 中了解到的內(nèi)容,可以毫不牽強(qiáng)地說在使用 JSF 的開發(fā)中不使用 Seam 是反常的。作為進(jìn)一步的證明,只需看看 JSR 299, Web Beans 的投票結(jié)果(見 參考資料)。 顯然,在不久的將來,Seam 會成為一個官方規(guī)范,Java EE 棧最終將提供 “顯著簡化的基于 Web 的應(yīng)用程序編程模型”。這對 JSF 開發(fā)人員和 Seam 來說是一個好消息。但是,即使沒有聲明要成為一個 Java 標(biāo)準(zhǔn),Seam 也是 JSF 的一個有價值的補(bǔ)充。

Seam 只需很少的設(shè)置就可以開始用于 JSF —— 而正是這一點小小的付出,就能解決 JSF 開發(fā)中的一些最麻煩的難題。回報勝于付出 —— 這里討論的 Seam 的優(yōu)點還只是一個開始。






回頁首


下載

描述名字大小下載方法
Barebones Seam 項目1 j-seam1.zip 13KB HTTP
關(guān)于下載方法的信息

注意:

  1. 該項目框架使用 Maven 2 構(gòu)建基礎(chǔ)設(shè)施。當(dāng)構(gòu)建執(zhí)行時,所有依賴項都將按需獲取。


參考資料

學(xué)習(xí)

獲得產(chǎn)品和技術(shù)

討論


關(guān)于作者

Dan allen

Dan Allen 目前是 CodeRyte 的一名高級 Java 工程師。他還是一名熱情的開放源碼擁護(hù)者,每當(dāng)他看到企鵝時就會有一點瘋狂。 從 Cornell 大學(xué)畢業(yè)并獲得材料科學(xué)與工程學(xué)位,Dan 對 Linux 和開放源碼軟件非常著迷。從那以后,他就沉浸在 Web 應(yīng)用程序領(lǐng)域,最近幾年則專攻 Java 相關(guān)的技術(shù),包括 Spring、Hibernate、Maven 2 和豐富的 JSF 堆棧。您可以在 http://www. 訂閱 Dan 的 blog,以跟蹤他的開發(fā)經(jīng)驗。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多