Seam無縫整合 JSF: 藉助 Seam 進行對話
藉助 Seam 開發有狀態的 CRUD 應用程式是件輕而易舉的事情。在 無縫 JSF 系列文章的第二篇中,Dan Allen 向您展示如何使用 Java™Server Faces (JSF) 和 Seam 為基於 Web 的高爾夫課程目錄開發建立、讀取、更新和刪除用例。在此過程中,他突出強調了 Seam 對 JSF 生命週期的兩項增強功能 —— 也就是 conversation 作用域和通過自定義 Java 5 註釋進行配置 —— 並解釋了其能夠降低伺服器負載和縮減開發時間的原因。
在這個分為三部分的系列文章的第一篇中介紹了 Seam,它是既能顯著增強 JSF 功能又能實現基於元件的架構的應用程式框架。在這篇文章中,我解釋了 Seam 和其他經常與 JSF 結合使用的 Web 框架的不同之處,展示了向現有 JSF 應用程式新增 Seam 是多麼輕鬆,最後概述了 Seam 對 JSF 應用程式生命週期的增強,同時還涉及到有狀態的對話、工廠元件以及使用註釋進行隱祕配置。
儘管這篇文章可能引發了您對 Seam 的興趣,但是您可能無法確信它能夠改善 JSF 開發體驗。整合一組新工具通常比閱讀它複雜得多,並且有時候並不值得。在無縫 JSF 系列文章的第二篇文章中,您將親自發現 Seam 是否能夠實現其簡化 JSF 開發的承諾。在使用 Seam 構建執行標準 CRUD 操作的簡單應用程式之後,我敢肯定您會認為 Seam 是對 JSF 框架的必要擴充套件。結果,Seam 還能幫助降低資料庫層有限資源的負載。
|
Open 18 是基於 Web 的應用程式,允許使用者管理一列曾經體驗過的高爾夫課程,並記錄每個場次的分數。為了體現本討論的目的,該應用程式的範圍僅限於管理高爾夫課程目錄。第一個螢幕展現了已經輸入的課程列表,並列出各個課程的一些相關欄位,如課程名稱、地點和專賣店的電話號碼。使用者可以從該處檢視完整的課程詳細內容、新增新課程、編輯現有課程,最終還可以刪除課程。
在講述如何使用 Seam 為 Open 18 應用程式開發用例時,我重點講述它如何簡化程式碼,自動管理一系列請求期間的狀態,並對輸入資料執行資料模型驗證。
該系列文章的目標之一是證明 Seam 可以整合到現有的任何 JSF 應用程式,並且不需要轉換到 Enterprise JavaBeans (EJB) 3。因此,Open 18 應用程式並不依靠 Seam 的 JPA EntityManager 整合進行事務型資料庫訪問,也不依靠 EBJ3 有狀態會話 bean 進行狀態管理。(Seam 附帶的示例 大多都使用了這兩項技術。)Open 18 設計為使用無狀態的分層架構。服務層和資料訪問 (DAO) 層使用 Spring 框架繫結到一起。我相信由於 Spring 在 Web 應用程式領域的普遍性,該設計是切實可行的選擇。該應用程式展示瞭如何通過使用 conversation 作用域將有狀態的行為引入到 JSF 託管的 bean。記住這些 bean 是簡單的 POJO。
您可以 下載 Open 18 原始檔 以及 Maven 2,以編譯並執行樣例程式碼。為了使您快速入門,我已經將該應用程式配置為使用 Seam 和 Spring-JSF 整合。如果想要在自己的專案中設定 Seam,可以在 本系列第一篇文章 中找到完整的操作指導。請參見 參考資料 瞭解關於整合 JSF 和 Spring 的更多資訊。
構建利用 Spring 框架的 JSF 應用程式的第一個步驟是配置 JSF,使其可以訪問 Spring 容器中的 bean。spring-web 包是 Spring 釋出的一部分,附帶有自定義 JSF 變數解析器,可構建此橋樑。首先,Spring 解析器委託給 JSF 實現附帶的本地解析器。本地解析器嘗試將值繫結引用(如 #{courseManager})與 JSF 容器中的託管 bean 相匹配。該 bean 名稱是由 #{} 表示式分隔符之間的字元組成的,在這個例子中為 courseManager。如果該查詢未能發現匹配,自定義解析器就會檢查 Spring 的 WebApplicationContext,以查詢帶有匹配 id 屬性的 Spring bean。請記住 Seam 是 JSF 框架的擴充套件,因此 JSF 可以訪問的任何變數也可以被 Seam 訪問。
Spring 變數解析器是使用變數解析器節點在 faces-config.xml 檔案中配置的,如清單1所示:
清單 1. 配置 spring 變數解析器
|
|
為了體現本文的目的,我假設基於 Spring 的服務層是不證自明的。除了 JSF-Spring 整合層之外 —— 該層負責向 JSF 公開 Spring bean (因此也向 Seam 公開該 bean),並沒有深入地使用 Spring。服務層物件將作為無狀態的介面對待,CRUD 操作可以委託給該介面。解決這些應用程式細節之後,就可以自由地重點研究 Seam 如何將託管 bean 轉換成有狀態的元件,這些元件明確其在促進使用者與應用程式互動方面的角色。
通過建立名為 courseAction 的支援 bean 來支援管理高爾夫課程目錄的檢視,就開始開發 Open 18 應用程式。該託管 bean 公開一個高爾夫課程物件集合,然後對管理這些例項的操作做出響應。這些資料的持久化委託給基於 Spring 的服務層。
在典型的 JSF 應用程式中,使用託管 bean 工具來註冊 CourseAction bean,然後藉助其委託物件(或 “依賴項”)注入該 bean。為此,必須開啟 faces-config.xml 檔案,然後使用該 bean 的名稱和類新增新的 managed-bean 節點,如清單 2 所示。通過使用值繫結表示式新增引用其他託管 bean 的子 managed-property 節點,指定要向該類的屬性中注入的依賴項。在這個例子中,惟一的依賴項是無狀態的服務物件 courseManager,它是使用來自 Appfuse 專案的 GenericManager 類實現的(請參見 參考資料)。
清單 2. 作為 JDF 託管 bean 定義的 CourseAction
|
現在您想起了使用本地 JSF 方法定義託管 bean 有多麻煩,請忘記曾經看到 managed-bean XML 宣告 —— 因為您不再需要它了!在 Seam 構建的應用程式中,bean 僅僅是使用 Java 5 註釋宣告的。Seam 將這些 bean 稱為上下文元件。儘管您可能覺得該術語很深奧,但是它只是描述一個元件(或命名例項)與給定的作用域(或稱為上下文)有關。
Seam 在為上下文元件分配的作用域的生命期內對該元件進行管理。 Seam 元件更像 Spring bean,而不是 JSF 託管 bean,這是因為它們插入到複雜的、面向方面的框架。在功能方面,Seam 框架遠勝於 JSF 的基本控制反轉 (IOC) 容器。觀察清單 3 中 courseAction 的宣告。CourseAction 類被重構為利用 Seam 的註釋。
清單 3. 作為 Seam 元件定義的 CourseAction
@Name("courseAction") public class CourseAction { @In("#{courseManager}") private GenericManager |
|
注意所有 XML 語句都被去掉了!總之,這就是 Seam 註釋的美妙之處。類的 @Name 註釋指導 Seam 的變數解析器處理名稱與註釋中的值相匹配的變數請求。然後 Seam 例項化這個類的例項,注入 @In 註釋指派的任何依賴項,然後假借該變數名公開該例項。使用清單 3 作為示例,Seam 建立了 CourseAction 類例項,將 courseManager Spring bean 注入courseManager 屬性,然後在收到對變數 courseAction 的請求時返回該例項。額外的好處是,該 bean 的配置接近於程式碼,因此對繼承程式碼庫的新開發人員來說更加透明(甚至對於您這樣只學了 6 個月的人來說也是如此)。
@In 註釋告知 Seam 將繫結表示式 #{courseManager} 的值注入到定義它的屬性。安裝 JSF-Spring 整合之後,該表示式解析成 Spring bean 配置中定義的名為 courseManager 的 bean。
|
既然已經準備就緒,就可以繼續研究第一個用例。在 Open 18 應用程式的開始螢幕中,向使用者提供了當前儲存在資料庫中的所有課程列表。藉助 h:dataTable 元件標記,清單 4 中的頁面定義相當直觀,並且不允許任何 Seam 特有的元素:
清單 4. 初始課程列表檢視
|
Java 程式碼可能有點難懂。清單 5 展示瞭如何使用本地 JSF 在作用域為該請求的支援 bean 中準備一個課程集合。為了簡便起見,去掉了注入的 Spring bean。
清單 5. 作為 DataModel 公開課程
public class CourseAction { // ... private DataModel coursesModel = new ListDataModel(); public DataModel getCourses() { System.out.println("Retrieving courses..."); coursesModel.setWrappedData(courseManager.getAll()); return coursesModel; } public void setCourses(DataModel coursesModel) { this.coursesModel = coursesModel; } } |
清單 5 中的 Java 程式碼看起來相當直觀,不是嗎?下面研究 JSF 使用支援 bean 時帶來的效能問題。提供實體列表時,您可能使用兩種方法之一。第一種是應用條件邏輯呈現至少包含一項的集合所支援的 h:dataTable ,第二種是顯示一條資訊型訊息,宣告找不到任何實體。要做出決定,可能需要諮詢 #{courseAction.courses},然後再對支援 bean 呼叫相關的 getter 方法。
如果載入截至目前所開發的頁面,然後檢視最終的伺服器日誌輸出,就會看到:
Retrieving courses... Retrieving courses... Retrieving courses... |
那麼兄弟!如果您將這些程式碼投入生產,最好能找到一個 DBA 找不到的安全隱藏點!這類程式碼執行對於資料庫來說是個負累。更糟的是,回發時情況會惡化,此時可能發生額外的冗餘資料庫呼叫。
如果曾經使用 JSF 開發過應用程式,就會了解盲目地在 getter 方法中獲取資料非常不妥。為什麼?因為在典型的 JSF 執行生命週期中,會多次呼叫 getter 方法。工作區嘗試通過委託物件使資料檢索過程與後續的資料訪問過程相隔離。其目的是避免每次諮詢支援 bean 的訪問函式時帶來執行查詢的計算成本。解決方案包括在建構函式中初始化 DataModel(靜態塊),或 “init” 託管屬性;在該 bean 的私有屬性中快取結果;使用 HttpSession 或作用域為會話的支援 bean;並依賴另一層 O/R 快取機制。
清單 6 顯示了另一種選擇:使用作用域為該請求的 bean 的私有屬性臨時快取查詢結果。您會發現,這至少能夠在頁面呈現階段消除冗餘獲取,但是當該 bean 在後續頁面超出作用域時,仍然會丟棄該快取。
清單 6. 作為 DataModel 公開課程,僅獲取一次
public class CourseAction { // ... private DataModel coursesModel = null; public DataModel getCourses() { if (coursesModel == null) { System.out.println("Retrieving courses..."); coursesModel = new ListDataModel(courseManager.getAll()); } return coursesModel; } public void setCourses(DataModel coursesModel) { this.coursesModel = coursesModel; } } |
清單 6 中的方法只是切斷資料檢索和資料訪問的嘗試之一。無論您制定什麼樣的解決方案,保持資料的可用性直到不再需要是避免冗餘資料獲取的關鍵。幸運的是,這類上下文狀態管理正是 Seam 所擅長的!
|
Seam 使用工廠模式初始化非元件物件和集合。一旦初始化資料之後,Seam 就可以將生成的物件放到一個可用的作用域中,然後就可以在其中反覆讀取,而不再需要藉助工廠方法。這個特殊的上下文就是 conversation 作用域。conversation 作用域提供了在一組明確定義的請求期間臨時維護狀態的方法。
直到最近,也很少有 Web 應用程式架構提供任何型別的能夠表現對話的構造。現有的任何上下文都沒有提供合適的粒度水平,用於處理多請求操作。您會發現,對話提供了一種方式,可以防止短期儲存丟失,而短期儲存丟失在 Web 應用程式中很常見,並且還是濫用資料庫的根本原因。結合工廠元件模式使用對話使得在合適時諮詢資料庫成為可能,而不是為了重新獲取應用程式未能跟蹤的資料。
|
要完成一項任務,應用程式常常必須指導使用者瀏覽一系列螢幕。該過程通常需要多次向伺服器發出 post,或者是由使用者直接提交表單,或者通過 Ajax 請求。在任何一種情況下,都應該能夠在用例期間通過維護伺服器端物件的狀態跟蹤該應用程式。對話相當於邏輯工作單元。它允許您藉助確定的起始點和結束點在單個瀏覽器視窗中為單個使用者建立單獨的上下文。使用者與該應用程式的互動狀態是針對整個對話維護的。
Seam 提供了兩類對話:臨時對話和長時間執行的對話。臨時對話 存在於整個請求過程,包括重定向。這項功能解決了 JSF 開發過程中的一項難題,即重定向將無意中丟棄儲存在 FacesContext(如 FacesMessage 例項)中的資訊。臨時對話是 Seam 中的標準操作模式:您可以免費獲得這些模式。這意味著在經過重定向之後取出的任何值仍然能夠存在,而不需要您執行額外的工作。這項功能是安全網,允許 Seam 自由地在任意適當的時候使用重定向。
相比之下,長期執行的對話 能夠在一系列明確定義的請求期間保持作用域中的變數。您可以在配置檔案中定義對話邊界,藉助註釋進行宣告,也可以藉助 Seam API,通過程式設計對其進行控制。長期執行的對話有點像小會話,隔離在自己的瀏覽器選項卡中(或視窗),能夠在對話結束或超時時自動清除。與對應的會話相比,conversation 作用域的要點之一是:conversation 作用域將發生在同一應用程式螢幕上位於多個瀏覽器選項卡中的活動分離開。簡單地講,使用對話消除了併發衝突的危險。(請參見 參考資料 閱讀關於 Seam 如何隔離併發對話的詳細討論。)
Seam 對話是對 ad-hoc 會話管理方法的重大改進,後者是現場臨時準備的,或者是其他框架鼓勵使用的。conversation 作用域的引入還解決了很多開發人員指出的問題,即 JSF 使用物件打亂了 HttpSession,沒有提供任何自動垃圾回收 (GC) 機制。對話允許您建立有狀態的元件,而不必使用 HttpSession。藉助 conversation 作用域,幾乎不再需要使用會話作用域,並且您可以更為隨意地使用。
|
回到課程列表示例,這時該重構程式碼,以利用工廠模式。目的是允許 Seam 管理課程集合,以便其在請求(包括重定向)期間保持可用。如果希望 Seam 管理該集合,則必須使用合適的註釋將建立過程交給 Seam。
Seam 使用構建函式例項化和裝配元件。這些構建函式是在 bean 類中通過註釋宣告的。實際上,您已經見到過其中一個例子: @Name 註釋。@Name 註釋告知 Seam 使用預設的類建構函式建立新例項。要構建自己的課程列表,您不希望使用元件例項,而是使用物件集合。為此,您希望使用 @Factory 註釋。@Factory 註釋向已提取變數的建立過程附加了一個方法,這是在註釋的值中指定的,當該變數沒有繫結任何值時就會使用該方法。
在清單 7 中,工廠方法 findCourses()(位於 CourseAction 類)用於初始化 courses 屬性的值,該值是作為 DataModel 提取到檢視中的。該工廠方法通過將這項工作委託給服務層來例項化課程物件集合。
清單 7. 使用 DataModel 註釋公開課程
@Name("courseAction") public class CourseAction { // ... @DataModel private List |
請注意,這裡不存在 getCourses() 和 setCourses()方法!藉助 Seam,使用標記著 @DataModel 註釋的私有屬性的名稱和值將資料提取到檢視中。因此不需要屬性訪問函式。在這個方案中,@DataModel 註釋執行兩項功能。首先,它提取或公開 該屬性,以便 JSF 變數解析器可以通過值繫結表示式 #{courses} 對它進行訪問。其次,它提供了手動在 DataModel 型別中包裝課程列表的備選方式(如 清單 4 中所示)。作為替代,Seam 自動在 DataModel 例項中嵌入課程列表,以便其可以方便地與 UIData 元件(如 h:dataTable)一起使用。因此,支援 bean(CourseAction)成為簡單的 POJO。然後由該框架處理 JSF 特有的細節。
清單 8 顯示了該檢視中發生的相應重構。與 清單 5 惟一的不同之處在於值繫結表示式。利用 Seam 的提取機制時,使用縮寫的值繫結表示式 #{courses} ,而不是通過 #{courseAction.courses} 諮詢支援 bean 的訪問方法。提取的變數直接放到該變數上下文中,不受其支援 bean 的約束。
清單 8. 使用提取的 DataModel 的課程列表檢視
|
現在再次訪問該頁面時,以下訊息在控制檯中只出現一次:
Retrieving courses... |
使用工廠構建函式以及臨時 conversation 作用域能夠在請求期間保持這些資料,並確保變數 courses 僅例項化一次,而不管在檢視中它被訪問了多少次。
您可能想知道 @Factory 註釋什麼時候起作用。為了防止註釋變得太神祕,我們將逐步分析剛剛描述的建立方案。可以按照圖 1 中的序列圖進行研究:
圖 1. Seam 提取使用工廠方法初始化的 DataModel
檢視元件(如 h:dataTable)依靠值繫結表示式 #{courses} 提供課程集合。本地 JSF 變數解析器首先查詢與名稱 courses 相匹配的 JSF 託管 bean。如果找不到任何匹配,Seam 就會收到解析該變數的請求。Seam 搜尋其元件,然後發現在 CourseAction 類中,@DataModel 註釋被指派給具有等價名稱(courses)的屬性。然後如果不存在 CourseAction 類例項,則建立之。
如果 courses 屬性的值為 null,Seam 就會再次使用該屬性的名稱作為鍵查詢 @Factory 註釋。藉助 findCourses() 方法找到匹配之後,Seam 呼叫它來初始化該變數。最後作為 courses 提取該屬性的值,將其包裝到 DataModel 例項。現在 JSF 變數解析器和檢視就可以使用包裝的值。任何針對此上下文變數的後續請求都會返回已經準備好的課程集合。
既然已經清楚檢索課程列表以及在 Seam 託管的上下文變數中維護該值的方法,下面研究課程列表以外的內容。您已經準備好與課程目錄進行互動。在以下幾節中,將使用顯示單門課程詳細內容(以及新增、編輯和刪除課程)的功能,擴充套件 Open 18 應用程式。
|
遇到的第一項 CRUD 操作是顯示從課程列表中選出的單門課程的詳細內容。 JSF 規範實際上為您處理了一些資料選擇收集工作。當從 UIData 元件(如h:dataTable)的某行觸發 h:commandLink 之類的操作時,在呼叫事件監聽程式之前,元件的當前行設定為與該事件相關的行。可以將當前行想象成一個指標,在這個例子中,該指標固定在接受該操作的行。實際上,JSF 瞭解行操作與該行的底層資料有關。處理該操作時,JSF 幫助將這些資料放到上下文中。
JSF 本身允許您以兩種方式訪問支援被啟用行的資料。一種方式是使用 DataModel#getRowData() 方法檢索該資料。另一種方法是從對應於臨時迴圈變數的值繫結中讀取該資料,該變數定義在元件標記的 var 屬性中。在第二種情況下,在事件處理期間將再次向變數解析器公開臨時迴圈變數(_course)。這兩種訪問形式最終都需要與 JSF API 進行互動。
如果選擇 DataModel API 作為行資料入口點,那麼必須將 DataModel 包裝器物件公開為支援 bean 的屬性,如 清單 4 所示。另一方面,如果選擇通過值繫結訪問行資料,則必須諮詢 JSF 變數解析器。後一種方法還會將您與檢視中使用的臨時迴圈變數名稱 _course 聯絡起來。
現在考慮 Seam 更抽象的獲得所選資料的方法。Seam 允許您將針對 Seam 元件定義的 @DataModel 註釋與 @DataModelSelection 補充註釋配對。在回發期間,Seam 自動檢測該配對。然後將 UIData 元件的當前行資料注入指派了 @DataModelSelection 註釋的屬性。該方法使支援 bean 與 JSF API 分離,因此使其返回 POJO 狀態。
|
要確保回發時該課程列表仍然可用,並且不必重新從資料庫中獲取該列表,就能呈現下一個響應,則必須將當前的臨時對話轉變成長期執行的對話。
說服 Seam 將臨時對話提升到長期執行對話的一種方式是設定一個方法,使其在執行過程中駐留 @Begin 註釋。還必須將元件本身放到該 conversation 作用域中。通過在 CourseAction 類定義頂部新增 @Scope(ScopeType.CONVERSATION) 註釋,就可以實現。使用長期執行的對話,允許變數保持作用域直至對話結束,而不僅僅是單個請求。對於 UIData 元件來說,這種跨多個請求的穩定性尤其重要。(請參閱 本系列第一篇文章 中關於有狀態元件的討論,瞭解資料不穩定可能對 UIData 元件的列隊執行事件所造成的問題。)
您希望允許使用者從課程目錄中選擇單個課程。要實現這項功能,在 h:commandLink 中包裝各個課程的名稱,h:commandLink 將方法繫結 #{courseAction.selectCourse} 指派成操作,如清單 9 所示。當使用者單擊其中一個連結時,就會觸發對支援 bean 的 selectCourse() 方法的呼叫過程。由於 Seam 控制著注入過程,所以與該行有關的課程資料將自動分配給帶有 @DataModelSelection 註釋的屬性。因此,不必執行任何查詢,就能使用該屬性,詳細資訊如清單 10 所示。
清單 9. 新增命令連結以選擇課程
|
向提供資料選擇的支援 bean 新增的內容主要是註釋;放到 conversation 作用域時,必須將該類序列化。
清單 10. 用於捕獲所選課程的 DataModelSelection 註釋
@Name("courseAction") @Scope(ScopeType.CONVERSATION) public class CourseAction implements Serializable { // ... @DataModel private List |
|
在 清單 10 中可以看出,所有變數作用域是由 Seam 處理的。當執行工廠方法來初始化課程集合時,Seam 遇到 @Begin 註釋,因此將該臨時對話提升為長期執行的對話。@DataModel 註釋提取的變數採用其所有者元件的作用域。因此,在對話期間,該課程集合保持可用。當遇到標記著 @End 註釋的方法時,對話結束。
單擊某一行的課程名稱時,Seam 使用支援該行的課程資料值填充帶有 @DataModelSelection 註釋的屬性。然後觸發操作方法 selectCourse(),導致在控制檯上顯示所選課程的名稱。最後,重新顯示課程列表。隨後就會在控制檯中看到:
Retrieving courses... Selected course: Sample Course Redirecting to /courses.jspx |
藉助 Seam,就不必在 faces-config.xml 中定義導航規則,即對映每個操作的返回值。取而代之,Seam 檢查操作的返回值是不是有效的檢視模板(技術上稱之為檢視 id),並對其執行動態導航。這項功能能夠使簡單的應用程式保持簡單,還允許對更高階的用例使用宣告式導航。請記住,在這個例子中,Seam 在執行導航時發出了重定向命令。
如果需要通過宣告結束對話,則可以使用 @End(beforeRedirect=true) 註釋操作方法 selectCourse(),在這種情況下,對話會在每次呼叫該方法後結束。beforeRedirect 屬性確保在呈現下一個頁面之前清除對話上下文中的變數,這樣能使臨時對話的工作短路,而在重定向時臨時對話通常會填充這些值。在這個方案中,在每次選中課程時開始資料準備過程。執行完以上描述的同一事件序列之後,現在控制檯將顯示:
Retrieving courses... Selected course: Sample Course Redirecting to /courses.jspx Retrieving courses... |
|
您尚未詳細瞭解顯示課程的用例。@DataModelSelection 註釋負責將當前行資料注入支援 bean 的例項變數,但是它不是在執行該操作方法之後填充資料,使其可用於隨後的檢視。為此,必須提取所選的值。
您已經看到一種注入形式,即 @DataModel 註釋向要呈現的檢視公開一個物件集合。@DataModel 註釋對單個物件例項的補充是 @Out 註釋。@Out 註釋僅僅獲取該屬性,並使用該屬性自己的名稱向變數解析器公開其值。預設情況下,每次啟用時,@Out 註釋都需要非 null 值。因為並非總是存在課程選擇,如第一次顯示課程列表時,所以必須將所需的註釋標記設定為 false,以表明該提取是有條件的。
|
預設情況下,@Out 註釋反映了用於確定上下文變數名稱的屬性名稱。如果您認為更合適的話,可以選擇為提取的變數使用不同名稱。因為課程資料將被提取到 conversation 作用域,並且可能在後續的一些請求中使用,所以該名稱的 “所選” 特徵失去了原來的意義。在這種情況下,最好使用實體本身的名稱。因此,selectedCourse 屬性的推薦註釋為 @Out(value="course", required=false)。
可以在新頁面上顯示課程詳細內容,也可以顯示在同一頁面的表格下面。為了演示的目的,在同一頁面顯示詳細內容,同時限制要構造的檢視數目。要在另一個頁面中訪問提取的變數,不需要額外的工作或特殊技巧。
與 該支援 bean 的上一個版本 的差別不大,因此,清單 11 僅突出顯示了兩者的不同之處。selectedCourse 屬性現在有兩個註釋。selectCourse() 方法也被稍加整理。現在它在繼續呈現檢視之前重新提取該課程物件。在無狀態的設計中,必須確保完全由資料層填充物件,並且正確地初始化任何與顯示其詳細內容有關的延遲載入。
清單 11. 將所選課程提取到檢視
// ... @DataModelSelection @Out(value="course", required=false) private Course selectedCourse; public String selectCourse() { System.out.println("Selected course: " + selectedCourse.getName()); // refetch the course, loading all lazy associations selectedCourse = courseManager.get(selectedCourse.getId()); System.out.println("Redirecting to /courses.jspx"); return "/courses.jspx"; } // ... |
其中大多數有趣的變化都發生在檢視中,但是這些變化並不新奇。清單 12 顯示了在選中某個課程時,呈現在 h:dataTable 下面的詳細內容皮膚:
清單 12. 有條件地為所選課程顯示課程詳細內容
|
相關文章
- JSF專案中seam的配置JS
- JSF的加減法與SeamJS
- Seam中的JSF表單驗證JS
- Wicket、Grails與JSF/seam, tapestry效能比較AIJS
- Seam: 為 JSF 量身定做的應用程式框架JS框架
- JBoss Seam:一個深度整合框架(二)框架
- JBoss Seam:一個深度整合框架(一)框架
- seam2.0整合resteasy框架的配置REST框架
- 配置Seam元件元件
- JBoss釋出了支援Groovy並增強了JSF的Seam 2.0JS
- 藉助 webpack 對專案進行分析優化Web優化
- Seam 2.0正式釋出
- Seam應用程式框架框架
- seam中使用多執行緒的問題執行緒
- 關於JBOSS的SEAM框架框架
- Seam @Name 建立元件的方式?元件
- Gavin King真正走上EJB路線,推出基於JSF/EJB3的快速開發框架JBoss SeamJS框架
- tomcat中部署seam期待高手告訴小弟Tomcat
- 翻譯:JBoss Seam 2.0蹦達出來了
- 從Python & Zope到Java with EJB 3 & JBoss SEAMPythonJava
- 藉助Python 函式進行模組化程式碼Python函式
- The Seam: J2EE 5.0 應用開發框架框架
- 有JBOSS seam的詳細開發資料嗎
- 請大家關注一下Jboss seam專案
- 藉助代理IP進行網頁抓取的終極指南網頁
- fasterWhisper和MoneyPrinterPlus無縫整合AST
- jbuilder 2006 除錯Seam的booking例程UI除錯
- 資料無縫對接:金蝶雲星空與旺店通整合案例
- 500 GPM Drilling fluid system for coal seam Methane and HDDUI
- seam 後臺報FullTextSessionProxy的錯誤是怎麼回事呢?Session
- 與 Python 無縫整合:基本特殊方法 1Python
- 與 Python 無縫整合:基本特殊方法(1)Python
- 教育公司藉助AI對話分析系統,提升銷售效率分三步走AI
- 關注JBoss SEAM 1.0,重新定義的web應用架構Web應用架構
- 如何在終端介面藉助圖形化工具進行資料分析
- Parallels Desktop 19 for Mac:無縫整合Windows和MacOSParallelMacWindows
- 《Rcpp:R與C++的無縫整合》已出版C++
- 5.3. 驗證 Seam安全中的驗證特性是基於JAAS