- Spring Web Flow:
- 是一個Web的框架,適用於元素按規定流程運行的process
- Struts沒辦法將flow及implementation 分離,flow的定義分散在組成流程的各個元素中。沒有一個地方能透完整描述整個流程
- Spring Web Flow是Spring MVC的extension,support基於流程的application,流程的定義及implement flow behavior的class及view能夠分開
- 使用Spring Web Flow做一個pizza order web application
- Config Web Flow in Spring
- Spring Web Flow是built在Spring MVC之上,這表示所有flow request都需要經過Spring MVC的DispatcherServlet。因此需要在Spring application context中配置bean來處理request並執行flow
- 目前版本還無法在Java中配置Spring Web Flow,所以只能在XML中進行配置
- 有一些bean會使用Spring Web Flow的Spring XML namespace,所以需要先進行定義
- 這樣就可以wire up web flow的bean
- Wiring a flow executor
- flow executor流程執行器,驅動flow的執行。
- 當client進入一個flow的時候,flow executor會為client建立並且啟動一個flow executor
- 當流程暫停的時候(比如在向client display view的時候),flow executor會在client執行操作後恢復flow
- 在Spring中,<flow:flow-executor> element會創造一個flow executor
- flow executor只負責create及execute flow,並不負責load flow definition,這個責任在flow registry身上
- Configuring a flow registry
- flow registry的工作室load flow definition,讓flow executor能夠使用
- 可以在Spring中使用<flow:flow-registry>以配置 flow registry
- flow registry會在"/WEB-INF/flows"內找flow definition
- 根據<flow:flow-location-pattern>內的value,任何文件以-flow.xml結尾的xml的將視為flow definition
- 所有的flow都是透過ID引用,而flow的ID就是相對於base-path的路徑,也就是value內雙星號所代表的路徑
- 假設是/WEB-INF/flows/order/order-flow.xml,order是flow ID
- 也能夠移除base-path,直接定義file位置
- path attribute直接指明了/WEB-INF/flows/springpizza.xml作為flow definition,
- 這邊的ID就會是從file中獲得,在這裡就是springpizza
- 也能夠直接指定ID
- Handling flow requests
- DispatcherServlet會將request分配給controller;但對於flow而言,需要一個FlowHandlerMapping來幫助DispatcherServlet將flow request發送給Spring Web Flow
- Spring application context,FlowHandlerMapping的設定:
- wire flowRegistry的ref,這樣他就能知道如何將request的URL mapping到flow上
- 例如,有一個ID是pizza的flow,FlowHandlerMapping會知道如果request的URL是"/pizza",則就要mapping到此flow中
- FlowHandlerMapping的工作只是將flow request 轉給Spring Web Flow,而回應request的是FlowHandlerAdapter
- FlowHandlerAdapter相對於Spring MVC中的controller,他會回應發送的flow request,並且將其進行處理
- FlowHandlerAdapter也可以如下wire一個Spring bean
- 這個FlowHandlerAdapter是Spring Web Flow和DispatcherServlet之間的溝通橋樑,他會處理flow request並且管理基於這些request的flow。
- The components of a flow
- 在Spring Web Flow中,flow是由三個主要的element定義的: states, transitions, and flow data
- status: flow中事件發生的地點。假設flow是公路旅行,則status就是路途上的city,風景點及飯店等。這是一個業務邏輯執行,做出決策或將page展現給client的地方,而不是公路旅行中買chips及coke的地方
- transitions: 若status是旅行中停下來的地方,那麼transition就是連接這些點的公路。在flow中,通過transition從一個status轉移到另一個status
- flow data: 旅行中會需要買紀念品,留下一些記憶。類似的,flow處理中需要收集一些data(目前狀況)
- Status
- Spring定義五個不同的status,透過選擇Spring Web Flow的status幾乎可以把任意的安排功能夠造成session的web應用
- Action: flow邏輯發生的地方
- Decision: 將flow分成兩個方向
- End: 結束status的地方,進入這,flow將會結束
- Subflow: 當前flow啟動一個新的flow
- View: 暫停flow並invites user參與flow
- View state
- 目的:為user display message並使得user在flow中發揮作用
- 通常是JSP
- 使用<view-state>定義view status,id在此也指定了flow到這個status時要展現的logic view name應為welcome,id也表示flow到這時標示了這個status
- 也可以直接指定另一個view name
- 可以display一個form,如果希望指名form bind的對象,可以設定model attribute,這裡指定了takePayment view中的form將綁定於flowScope.paymentDetails
- Action state
- application自身在執行任務
- 一般會觸發Spring所管理的bean的一些method,並且根據method調用的結果轉到另為一個status
- 可以使用<action-state>
- 一般都會有<evaluate>,表達action status要做的事情
- expression指定了進入這個status要執行的expression,此例為SpEL expression,表示會找到ID是pizzaFlowActions的bean,並呼叫saveOrder() method
- SpEL是默認及推薦的expression language
- Decision state
- flow很有可能在某一個點根據flow的執行產生分枝。decision state將評估一個Boolean type的expression,然後在兩個state上選擇一個,這會取決於expression的結果是true or false
- 透過<decision-state>進行定義
- Subflow state
- 有可能不會將所有邏輯寫在一個method上,而是將其分散多個class、method其他結構中
- <subflow-state>可以在一個正在執行的flow中調用另外一個flow,就像一個class內的method呼叫另外一個method
- 如果subflow 的end-state id為orderCreated,則flow將會轉移到名為payment的state
- End state
- 所有的flow都要結束,<end-state>指定了結束
- 到達<end-state>表示flow會結束,接下來會發生什麼取決於一些因素
- 如果結束的是subflow,則會從調用它的flow <subflow-state>繼續執行,<end-state>的ID將會用來當作事情觸發,從<subflow-state>開始的轉移
- 如果<end-state>設定view attribute,則會轉向view,可以是相對於flow path的view template,如果加上externalRedirect: prefix,則會重新導向flow外部的page;如果加上flowRedirect: prefix,則會導向另外一個flow
- 如果不是1或2,則只是一個結束,browser最後會load flow的base URL address,會開始一個新的flow instance
- flow可能不只有一個end-state,有可能根據flow執行情況有不同的page可供選擇
- Transition
- flow中除了<end-state>以外的每個state,至少都需要一個transition,這樣就能知道這個state完成後flow要去哪裡
- state可以有多個transition,對應到當前state結束時可以執行的path
- <transition>可能是以下state element的子element
- <action-state>
- <view-state>
- <subflow-state>
- <transition>在flow中指定下一個state
- to attribute指定下一個state
- 更常見的transition是基於事情的觸發來進行
- 在任意的事件中,可以使用on attribute來指定觸發事件的transition
- 如果觸發了phoneEntered則flow將進入lookupCustomer
- 在拋出exception,flow也可以進入另一個state
- attribute on-exception就像是on attribute,只不過指定了發生transition的exception而不是event
- Global transition
- 與其在多個state都重複common transition,可以將<transition>作為<global-transition>的element,把他們定義為global transition
- 則所有state都會default使用這個cancel transition
- Flow Data
- declaring variables
- flow data保存在variable中,variable可以在flow的各個地方引用,能夠以多種方式建立。在flow中建立variable最簡單的方式是使用<var>
- 此範例建立一個Customer instance並將之放入customer variable中
- 使用<evaluate> create variable,下例<evaluate>計算一個SpEL expression,並將解果放入viewScope.toppingsList。這個variable是viewScope
- 也可以使用<set>,和<evaluate>很類似,都是將variable設置為expression的計算結果
- Scoping flow data
- flow中帶的data會有不同的scope和visibility,這決定於保存data的variable本身的scope,共五種scope
- Conversation
- Flow
- Request
- Flash
- View
- 當使用<var>時,此variable是flowScope,也就是在定義variable的flow內有效。當使用<set>或<evaluate>時,scope透過name的prefix指定
<?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/webflow-config http://www.springframework.org/schema/webflow-config/[CA] spring-webflow-config-2.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<flow:flow-executor id="flowExecutor" />
<flow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows"> <flow:flow-location-pattern value="*-flow.xml" /> </flow:flow-registry>
<flow:flow-registry id="flowRegistry"> <flow:flow-location path="/WEB-INF/flows/springpizza.xml" /> </flow:flow-registry>
<flow:flow-registry id="flowRegistry"> <flow:flow-location id="pizza" path="/WEB-INF/flows/springpizza.xml" /> </flow:flow-registry>
<bean class= "org.springframework.webflow.mvc.servlet.FlowHandlerMapping"> <property name="flowRegistry" ref="flowRegistry" /> </bean>
<bean class= "org.springframework.webflow.mvc.servlet.FlowHandlerAdapter"> <property name="flowExecutor" ref="flowExecutor" /> </bean>
<view-state id="welcome" />
<view-state id="welcome" view="greeting"/>
<view-state id="takePayment" model="flowScope.paymentDetails"/>
<action-state id="saveOrder"> <evaluate expression="pizzaFlowActions.saveOrder(order)" /> <transition to="thankYou" /> </action-state>
<decision-state id="checkDeliveryArea"> <if test="pizzaFlowActions.checkDeliveryArea(customer.zipCode)" then="addCustomer" // 指定state else="deliveryWarning" /> // 指定state </decision-state>
<subflow-state id="order" subflow="pizza/order"> <input name="order" value="order"/> <transition on="orderCreated" to="payment" /> </subflow-state>
<end-state id="customerReady" />
<transition to="customerReady" />
<transition on="phoneEntered" to="lookupCustomer"/>
<transition on-exception="com.springinaction.pizza.service.CustomerNotFoundException" to="registrationForm" />
<global-transitions> <transition on="cancel" to="endState" /> </global-transitions>
<var name="customer" class="com.springinaction.pizza.domain.Customer"/>
<evaluate result="viewScope.toppingsList" expression="T(com.springinaction.pizza.domain.Topping).asList()" />
<set name="flowScope.pizza" value="new com.springinaction.pizza.domain.Pizza()" />
沒有留言:
張貼留言