2017年12月26日 星期二

Java Spring - Spring Web Flow


  • 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,所以需要先進行定義
    • <?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">
      
    • 這樣就可以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:flow-executor id="flowExecutor" />
      
    • 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:flow-registry id="flowRegistry" base-path="/WEB-INF/flows">
        <flow:flow-location-pattern value="*-flow.xml" />
      </flow: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位置
    • <flow:flow-registry id="flowRegistry">
        <flow:flow-location path="/WEB-INF/flows/springpizza.xml" />
      </flow:flow-registry>
      
      • path attribute直接指明了/WEB-INF/flows/springpizza.xml作為flow definition,
      • 這邊的ID就會是從file中獲得,在這裡就是springpizza
    • 也能夠直接指定ID
    • <flow:flow-registry id="flowRegistry">
        <flow:flow-location id="pizza" path="/WEB-INF/flows/springpizza.xml" />
      </flow:flow-registry>
      
  • Handling flow requests
    • DispatcherServlet會將request分配給controller;但對於flow而言,需要一個FlowHandlerMapping來幫助DispatcherServlet將flow request發送給Spring Web Flow
    • Spring application context,FlowHandlerMapping的設定:
    • <bean class= "org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
        <property name="flowRegistry" ref="flowRegistry" />
      </bean>
      
      • 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
    • <bean class= "org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
        <property name="flowExecutor" ref="flowExecutor" />
      </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-state id="welcome" />
                  
                • 也可以直接指定另一個view name
                • <view-state id="welcome" view="greeting"/>
                • 可以display一個form,如果希望指名form bind的對象,可以設定model attribute,這裡指定了takePayment view中的form將綁定於flowScope.paymentDetails
                • <view-state id="takePayment" model="flowScope.paymentDetails"/>
              • Action state
                • application自身在執行任務
                • 一般會觸發Spring所管理的bean的一些method,並且根據method調用的結果轉到另為一個status
                • 可以使用<action-state>
                • <action-state id="saveOrder">
                    <evaluate expression="pizzaFlowActions.saveOrder(order)" />
                    <transition to="thankYou" />
                  </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>進行定義
                • <decision-state id="checkDeliveryArea">
                    <if test="pizzaFlowActions.checkDeliveryArea(customer.zipCode)"
                        then="addCustomer" // 指定state 
                        else="deliveryWarning" /> // 指定state 
                  </decision-state>
              • Subflow state
                • 有可能不會將所有邏輯寫在一個method上,而是將其分散多個class、method其他結構中
                • <subflow-state>可以在一個正在執行的flow中調用另外一個flow,就像一個class內的method呼叫另外一個method
                • <subflow-state id="order" subflow="pizza/order">
                    <input name="order" value="order"/>
                    <transition on="orderCreated" to="payment" />
                  </subflow-state>
                  
                • 如果subflow 的end-state id為orderCreated,則flow將會轉移到名為payment的state
              • End state
                • 所有的flow都要結束,<end-state>指定了結束
                • <end-state id="customerReady" />
                  
                • 到達<end-state>表示flow會結束,接下來會發生什麼取決於一些因素
                  1. 如果結束的是subflow,則會從調用它的flow <subflow-state>繼續執行,<end-state>的ID將會用來當作事情觸發,從<subflow-state>開始的轉移
                  2. 如果<end-state>設定view attribute,則會轉向view,可以是相對於flow path的view template,如果加上externalRedirect: prefix,則會重新導向flow外部的page;如果加上flowRedirect: prefix,則會導向另外一個flow
                  3. 如果不是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
              • <transition to="customerReady" />
                
                • to attribute指定下一個state
              • 更常見的transition是基於事情的觸發來進行
              • 在任意的事件中,可以使用on attribute來指定觸發事件的transition
              •  <transition on="phoneEntered" to="lookupCustomer"/>
                
                • 如果觸發了phoneEntered則flow將進入lookupCustomer
              • 在拋出exception,flow也可以進入另一個state
              •  <transition on-exception="com.springinaction.pizza.service.CustomerNotFoundException" to="registrationForm" />
                
              • attribute on-exception就像是on attribute,只不過指定了發生transition的exception而不是event
            • Global transition
              • 與其在多個state都重複common transition,可以將<transition>作為<global-transition>的element,把他們定義為global transition
              • <global-transitions>
                  <transition on="cancel" to="endState" />
                </global-transitions>
                
              • 則所有state都會default使用這個cancel transition
            • Flow Data
              • declaring variables
                • flow data保存在variable中,variable可以在flow的各個地方引用,能夠以多種方式建立。在flow中建立variable最簡單的方式是使用<var>
                • <var name="customer" class="com.springinaction.pizza.domain.Customer"/>
                  
                • 此範例建立一個Customer instance並將之放入customer variable中
                • 使用<evaluate> create variable,下例<evaluate>計算一個SpEL expression,並將解果放入viewScope.toppingsList。這個variable是viewScope
                • <evaluate result="viewScope.toppingsList" expression="T(com.springinaction.pizza.domain.Topping).asList()" />
                  
                • 也可以使用<set>,和<evaluate>很類似,都是將variable設置為expression的計算結果
                • <set name="flowScope.pizza" value="new com.springinaction.pizza.domain.Pizza()" />
                  
            • Scoping flow data
                • flow中帶的data會有不同的scope和visibility,這決定於保存data的variable本身的scope,共五種scope
                  1. Conversation 
                  2. Flow
                  3. Request 
                  4. Flash
                  5. View
                • 當使用<var>時,此variable是flowScope,也就是在定義variable的flow內有效。當使用<set>或<evaluate>時,scope透過name的prefix指定

            沒有留言:

            張貼留言