搶先一步
VMware 提供培訓和認證,以加速您的進度。
了解更多Spring Web Flow,就像 Spring Framework 本身一樣,是一種獨特的整合技術。 我們大多數的使用者將其視為一個通用的 ApplicationController,可以嵌入到任何環境中。 我們支援基於 Servlet 和 Portlet 的應用程式,並提供與領先的 Web 框架 Struts、Spring MVC 和 Java Server Faces 的整合。 甚至我知道有些團隊在 Flex 環境中使用 Spring Web Flow。 在這些環境中,Spring Web Flow 整合提供了更好的模型來實作導航邏輯和管理應用程式狀態。
我們的使用者喜歡這樣,因為他們可以編寫一次控制流程,並在任何地方重複使用。 在這個瞬息萬變的 Web 框架時代,Spring Web Flow 為他們提供了一個現代框架來學習和建立知識、工具和擴充功能。 從一開始,它就被設計為扮演這個角色,我很高興看到 Web Flow 整合在許多層面上不斷增長。
我們的整合不斷增長的一個重要領域是 Java Server Faces (JSF) 社群。 從 Spring Web Flow 1.0.3 開始,我們的 JSF 整合與 Spring 社群的期望一致,並提供了 JSF 開發者在實際應用中最需要的東西。 這個部落格將說明整合增強功能,以向您展示 Spring Web Flow 為 JSF 開發者帶來的改變。
Spring Web Flow 是一個控制器框架,它提供了一種語言和執行環境,用於在 Web 應用程式中實作使用者介面控制流程。 Java Server Faces 是一個使用者介面元件框架,它由一個標準 API 和兩個實作組成 - Sun Reference Implementation 和 Apache MyFaces。 作為 JCP 支援的標準 API 規範,JSF 提供了擴充點,允許產品供應商插入並競爭他們的擴充功能。 當用作 JSF 擴充功能時,Spring Web Flow 接管了兩個職責:處理您的檢視導航規則和管理與您正在進行的使用者互動相關的狀態(也稱為對話)。 這種整合將 Web Flow 在導航和狀態管理方面的優勢與 JSF 作為使用者介面元件庫不斷增長的生態系統的優勢結合在一起。 所有 JSF 元件和檢視都像以前一樣與 Web Flow 一起工作。 透過 Web Flow,JSF 開發者可以從更強大的導航模型中受益,該模型可以減輕傳統上與手動管理對話狀態相關的麻煩。
我可以滔滔不絕地談論 Web Flow 的特定功能集。 但我不打算這樣做,而是嘗試強調對 JSF 開發者影響最大的功能。
在導航處理領域,Web Flow 提供
基本上,Web Flow 解決了 這位可憐的人 在 JSF 的基本導航功能中遇到的每個問題。 正如 我們的一位領先使用者指出,Web Flow 可以用作 JSF 預設「以轉發為中心」導航模型的完整替代品
Jeremy Grelle:我一直在使用 SWF 作為 JSF 導航規則的完整替代品,即使是在我們更簡單的頁面和選單中也是如此。 我很高興能夠將其推向這種極端,因為讓我的團隊在多個地方定義導航規則會讓人感到困惑。
在狀態管理領域,Web Flow 引入了幾個對話範圍,這些範圍補充了 JSF 現有的請求、會話和應用程式範圍。 這些範圍是
回到我們領先的使用者所說的
Jeremy Grelle:我發現即使在 JSF 中更簡單的頁面中,您通常也需要一些東西來管理您的模型在對頁面的多個請求中的狀態...在 JSF 社群中有很多解決方案(Tomahawk 的 saveState 標籤、Shale 的對話框架等),但我個人認為 SWF 的多個範圍是最穩健和優雅的解決方案。
希望到目前為止,您對 Web Flow 作為 JSF 擴充功能時所帶來的東西有了一個很好的了解。 現在我想更詳細地介紹整合是如何工作的,然後以展示如何開始一起使用 Web Flow 和 JSF 來結束。
了解 Web Flow 如何插入 JSF 首先需要了解基本的 Web Flow 結構。 Spring Web Flow 中的控制器功能單元 - 類似於帳戶註冊精靈或客戶主檔/明細編輯器 - 稱為流程定義。 此類控制器的執行個體稱為流程執行。 在執行階段啟動一個新的流程執行,以允許單個使用者參與與應用程式的對話。 流程執行處理選擇使用者的初始檢視,然後回應使用者事件以執行應用程式行為並確定要顯示的下一個檢視。 它還管理與使用者對話相關聯的狀態。
將 Web Flow 與 JSF 整合的一個重要部分是將其流程執行生命週期擬合到 JSF 生命週期中。 這部分是透過實作一個 自訂 PhaseListener 來實現的,該 PhaseListener 處理啟動用戶端請求的新流程執行,以及在還原 JSF 檢視期間還原現有流程執行。
例如,透過存取類似於
http://localhost:8080/accounts/servlet.faces?_flowId=register-account-flow
...的 URL,您將啟動一個新的「register-account-flow」。 請求首先命中 FacesServlet,它啟動 JSF 生命週期。 在還原任何檢視之前,FlowPhaseListener 啟動「register-account-flow」,最終確定要顯示的初始 JSF 檢視。
一旦選擇了初始 JSF 檢視,它就會被呈現。 呈現通常發生在使用 Spring Web Flow 的預設 always redirect on pause 設置自動重新導向之後,這會將選定的檢視與一個 URL 關聯,該 URL 可以安全地重新整理並在稍後返回,而不會出現瀏覽器警告。
當呈現發生時,JSF UI 元件可以完全存取在任何 Web Flow 的對話範圍中管理的 bean,以及存取您的標準 JSF 會話和應用程式範圍。 元件值綁定是透明的,這意味著 JSF 檢視開發者不需要知道 bean 在哪個範圍內管理,他們只需要知道 bean 的名稱。 這種能力是透過實作另一個 JSF 擴充點(一個 自訂 VariableResolver)來實現的。
在參與流程執行的檢視呈現後,使用者會決定她想做什麼。 如果她決定點擊瀏覽器重新整理按鈕,那很好 - 流程執行會在其穩定的 URL 處重新整理,並且相同的 JSF 檢視會重新呈現。 如果她決定做一些事情來觸發來自同一頁面的一些 Ajax 請求,這也很好 - 流程執行會再次自動還原,並且參與 Ajax 呼叫的 JSF 元件可以無縫地以線程安全的方式更新流程管理狀態(沒有雙關語)。
一旦使用者決定呼叫一個 UI 命令,例如透過點擊一個命令連結,標準的 JSF 回傳生命週期就會啟動。 在處理 UI 元件驗證並更新模型值後,JSF 動作結果會作為一個事件發出,以反對已還原的流程執行的檢視狀態。 流程從那裡接管,透過呼叫適當的應用程式行為並根據流程定義的導航規則選擇下一個檢視來處理事件。 這個 Web Flow 導航步驟由 JSF 整合中的最後一個關鍵結構(一個 自訂 NavigationHandler)處理。
總而言之,從 JSF 視圖和元件開發人員的角度來看,這就是標準的 JSF - 無論您使用的是 JSP 或 Facelets 以及 XYZ 元件供應商都沒關係。 Web Flow 執行會自動為您還原,並且對話狀態的存取是完全透明的。 Web Flow 事件只是標準的 JSF UI 命令結果。 從控制器開發人員的角度來看,這一切都是 Web Flow。 因此,faces-config.xml 中冗長且有限的導覽規則被基於更豐富的流程定義語言的模組化、簡潔的流程定義所取代。 最重要的是,您可以獲得原生 JSF 元件模型的所有優勢,以及 Web Flow 導覽模型的所有優勢。
在 JSF 環境中開始使用 Spring Web Flow 的最佳方式是下載 1.0.3 版本並部署sellitem-jsf範例,該範例也可線上測試。 該範例可以作為 Eclipse Dynamic Web Project 導入,以便在 Eclipse 內部輕鬆部署。 此外,該範例已啟用 Spring IDE 2.0,這是一個包含圖形化 Web Flow 編輯器的 Eclipse 插件。
我將簡要介紹如何在 JSF 環境中使用 Web Flow 開始使用 sellitem-jsf 範例,以結束本文。
要將 Web Flow 插入,必須將上述討論的自定義集成工件添加到 faces-config.xml
<application>
<navigation-handler>org.springframework.webflow.executor.jsf.FlowNavigationHandler</navigation-handler>
<variable-resolver>org.springframework.webflow.executor.jsf.DelegatingFlowVariableResolver</variable-resolver>
</application>
<lifecycle>
<phase-listener>org.springframework.webflow.executor.jsf.FlowPhaseListener</phase-listener>
</lifecycle>
然後您需要定義控制器邏輯的流程定義。 這是“sellitem”流程定義,它是一個包含單個動態導航規則的 4 步驟結帳流程,最終以確認的銷售交易結束
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">
<var name="sale" class="org.springframework.webflow.samples.sellitem.Sale" scope="conversation" />
<start-state idref="enterPriceAndItemCount" />
<view-state id="enterPriceAndItemCount" view="/priceAndItemCountForm.jsp">
<transition on="submit" to="enterCategory"/>
</view-state>
<view-state id="enterCategory" view="/categoryForm.jsp">
<transition on="submit" to="requiresShipping" />
</view-state>
<decision-state id="requiresShipping">
<if test="${conversationScope.sale.shipping}" then="enterShippingDetails" else="processSale" />
</decision-state>
<view-state id="enterShippingDetails" view="/shippingDetailsForm.jsp">
<transition on="submit" to="processSale" />
</view-state>
<action-state id="processSale">
<bean-action bean="saleProcessor" method="process">
<method-arguments>
<argument expression="conversationScope.sale" />
</method-arguments>
</bean-action>
<transition on="success" to="showCostOverview" />
</action-state>
<end-state id="showCostOverview" view="/costOverview.jsp" />
</flow>
應將流程定義註冊到登錄檔中,以便它們有資格執行
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:flow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd">
<!-- Launches, continues, and refreshes flow executions -->
<flow:executor id="flowExecutor" registry-ref="flowRegistry"/>
<!-- Creates the registry of flow definitions eligible for execution in this application -->
<flow:registry id="flowRegistry">
<flow:location path="/WEB-INF/flows/sellitem-flow.xml" />
</flow:registry>
</beans>
註冊後,只需將瀏覽器指向帶有流程標識符作為輸入的 FacesServlet 即可啟動流程
http://localhost:8080/sellitem-jsf/servlet.faces?_flowId=sellitem-flow
值得注意的是,您可以配置流程定義和執行 URL 的格式 - 例如,開啟 REST 樣式的 URL 而不是預設的基於請求參數的 URL。
如前所述,JSF 視圖只是普通的 JSF 視圖,無論它們是基於 JSP 還是基於 Facelet,都具有用於存取對話狀態的標準 JSF 綁定表達式和用於發送信號 Web Flow 事件的標準 UI 命令。 sellitem-jsf 範例使用基於 JSP 的視圖
<f:view>
<div id="content">
<h2>Enter price and item count</h2>
<hr>
<table>
<h:form id="priceAndItemCountForm">
<tr>
<td>Price:</td>
<td>
<h:inputText id="price" value="#{sale.price}" required="true">
<f:validateDoubleRange minimum="0.01"/>
</h:inputText>
</td>
<td>
<h:message for="price" errorClass="error"/>
</td>
</tr>
<tr>
<td>Item count:</td>
<td>
<h:inputText id="itemCount" value="#{sale.itemCount}" required="true">
<f:validateLongRange minimum="1"/>
</h:inputText>
</td>
<td>
<h:message for="itemCount" errorClass="error"/>
</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<h:commandButton type="submit" value="Next" action="submit"/>
</td>
<td></td>
</tr>
</h:form>
</table>
</div>
</f:view>
就是這樣! 您可以試用該應用程式,親自了解 Web Flow 如何處理導覽和應用程式控制器邏輯,而 JSF UI 元件如何處理內容渲染、資料綁定和驗證行為。 這真的是一個很好的結合。
Spring Web Flow 1.1 的工作已經開始,更高層次的整合是令人興奮的路線圖中的一個主要主題。 在 JSF 方面值得注意的是,我們將在流程定義中添加對統一表達式語言 (EL) 的支援,以及支援利用 Spring 2.0 作為 JSF 管理 Bean 的全面提供者,涵蓋所有範圍 - 包括 Spring Web Flow 提供的對話範圍。 除了將使所有環境中的 Web Flow 使用者受益的新核心功能之外,還包括對話範圍持久性內容的支援。 參與論壇上的1.1 路線圖討論以獲取更多信息並參與其中!
我也想藉此機會鼓勵那些已經在 JSF 環境中使用 Spring Web Flow 的人說出您的經驗——給我發送電子郵件、在這裡發表評論、在 JSF Central 上撰寫文章、告訴 JSF 社區的領導者您的經驗。 在規範負責人徵求社區回饋意見時,您在現實世界中的經驗可以幫助影響 JSF 2.0 規範的方向。 Interface21 已收到 JSF 規範負責人 Ed Burns 的邀請,成為 JSF 2.0 專家組的一員,這證明了 Web Flow 作為一種創新的 JSF 擴展所做的貢獻。 我們接受了該邀請,並且很高興能夠幫助將在導航和狀態管理領域中經過驗證的有效方法以一般方式返回到 JSF 2.0,同時繼續開闢新領域並保持在任何環境中的可用性。