快人一步
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 參考實現和 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 中控制器功能的單元——例如賬戶註冊嚮導或客戶主/明細編輯器——被稱為一個 流程定義(flow definition)。此類控制器的執行時例項被稱為一個 流程執行(flow execution)。在執行時會啟動一個新的流程執行,允許單個使用者參與與應用的對話。流程執行負責選擇使用者的初始檢視,然後響應使用者事件以執行應用行為並確定要顯示的下一個檢視。它還管理與使用者對話相關的狀態。
將 Web Flow 與 JSF 整合的一個重要部分是將 Web Flow 的流程執行生命週期融入 JSF 生命週期。這部分是透過實現一個 自定義 PhaseListener 來實現的,該監聽器負責處理客戶端請求時啟動新的流程執行,以及在 JSF 檢視恢復期間恢復現有的流程執行。
例如,透過訪問如下 URL:
https://:8080/accounts/servlet.faces?_flowId=register-account-flow
... 你將啟動一個新的“註冊賬戶流程”(register-account-flow)。請求首先到達 FacesServlet,它會啟動 JSF 生命週期的程序。在任何檢視恢復之前,FlowPhaseListener 會啟動“register-account-flow”,最終決定要顯示的初始 JSF 檢視。
一旦選擇了初始 JSF 檢視,它就會被渲染。渲染通常發生在 Spring Web Flow 預設的 暫停時始終重定向 設定後的自動重定向之後,該設定將選定的檢視與一個 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 動態 Web 專案匯入,方便在 Eclipse 中部署。此外,該示例已啟用 Spring IDE 2.0,這是一個包含 圖形化 Web Flow 編輯器 的 Eclipse 外掛。
我將使用 sellitem-jsf 示例簡要介紹在 JSF 環境中開始使用 Web Flow 的方法,以此結束本文。
要接入 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,並將流程識別符號作為輸入即可啟動流程。
https://:8080/sellitem-jsf/servlet.faces?_flowId=sellitem-flow
值得注意的是,你可以配置流程定義和執行 URL 的格式——例如,啟用 REST 風格的 URL,而不是預設基於請求引數的 URL。
如前所述,JSF 檢視只是普通的 JSF 檢視,無論是基於 JSP 還是基於 Facelet,都使用標準的 JSF 繫結表示式來訪問會話狀態,並使用標準的 UI 命令來發送 Web Flow 事件。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 作為跨所有作用域(包括 Spring Web Flow 提供的會話作用域)的 JSF 受管 Bean 的全面提供者。這項工作是除了核心新功能之外的補充,這些新功能將使所有環境中的 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,同時繼續開拓新領域,並在任何環境中保持可用性。