Spring实战第八章学习笔记————使用Spring Web Flow
Spring实战第八章学习笔记————使用Spring Web Flow
Spring Web Flow是一个Web框架,它适用于元素按规定流程运行的程序。
其实我们可以使用任何WEB框架写流程化的应用程序,但是这样就没有办法将流程和实现分开了,你会发现流程的定义分散在组成流程的各个元素中,没有地方能够完整地描述整个流程。Spring Web Flow是Spring MVC的扩展,它支持开发基于流程的应用程序。它将流程的定义与实现流程行为的类和视图分离开来。
在Spring中配置Web Flow
Spring Web Flow是构建于SpringMVC基础上的,这意味着所有的流程请求都需要经过SpringMVC的DispatcherServlet。我们需要在Spring上下文中配置一些bean来处理流程请求并执行流程。shouxianzaiXML配置Spring Web 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"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
装配流程执行器
当用户进入一个流程时,流程执行器(flow executor)会为用户创建并启动一个流程执行实例。当流程暂停时,流程执行器会在用户操作后恢复流程。在Spring中,<flow:flow-executor>元素会创建一个流程执行器:
<flow:flow-executor id="flowExecutor" />
配置流程注册表
流程注册表(Flow registry)的工作是加载流程定义并让流程执行器能使用它们。在Spring中,<flow:flow-registry>元素会创建一个流程注册表:
<flow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows">
<flow:flow-location-pattern value="/**/*-flow.xml" />
</flow:flow-registry>
如这里声明的,流程注册表会在/WEB-INF/flows目录下寻找流程定义,这个路径是由base-path属性指明的。根据<flow:flow-location-pattern>元素,任何以-flow.xml结尾的XML文件都会被视为流程定义。
所有的流程都是通过其ID来进行引用的。使用<flow:flow-location-pattern>元素,流程的ID就是相对于base-path的路径,或者是双星号所代表的路径,如下图展示了流程ID是如何计算的:
处理流程请求
DispatcherServlet会将请求分发给控制器,Spring Web Flow中需要一个FlowHandlerMapping来帮助DispatcherServlet将流程请求发送给Spring Web Flow。
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
<property name="flowRegistry" ref="flowRegistry"/>
</bean>
然而,FlowHandlerMapping的工作仅仅是将流程请求定向到Spring Web Flow,响应请求的是FlowHandlerAdapter,它等同于Spring MVC的控制器,会对流程请求进行响应并处理。FlowHandlerAdapter可以像下面这样装配成一个Spring Bean:
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
<property name="flowExecutor" ref="flowExecutor" />
</bean>
流程的组件
在Spring Web Flow 中,流程是由三个主要元素定义的:状态、转义和流程数据。。流程中的状态是业务逻辑执行、做出决策或将页面展示给用户的地方。
状态
Spring Web Flow定义了五种不同类型的状态
| 状态类型 | 作用 |
|---|---|
| 行为(Action) | 流程逻辑发生的地方 |
| 决策(Decision) | 决策状态将流程分为两个方向,它会基于流程数据的评估结果确定流程方向 |
| 结束(End) | 结束状态是流程的最后一站,进入End状态,流程就会终止 |
| 子流程(Subflow) | 子流程状态会在当前正在运行的流程上下文中启动一个新的流程 |
| 视图(View) | 视图状态会暂停流程并邀请用户参与流程 |
视图状态
视图状态用于为用户展现信息并使用户在流程中发挥作用。在流程定义的XML文件中<view-state>用于定义视图状态:
<view-state id="welcome"/>
此时:id属性有两个含义。它在流程内标示这个状态。除此以外,因为在这里没有在其他地方指定视图,所以它也指定了流程到达这个状态时要展现的逻辑视图名为welcome。
如果你愿意显式指定另外一个视图名,那可以使用view属性做到这一点:
<view-state id="welcome" view="greeds"/>
如果流程为用户展现了一个表单,你可能希望指明表单所绑定的对象。为了做到这一点,可以设置model属性:
<view-state id="takePlayment" model="flowScope.paymentsDetails"/>
指定takePayment视图中的表单将绑定流程作用域内的paymentDetails对象。
行为状态
行为状态则是应用程序自身在执行任务。行为状态一般会触发Spring所管理bean的一些方法并根据方法调用的执行结果转移到另一个状态。在流程定义XML中,行为状态使用<action-state>元素来声明。
<action-state id="saveOrder">
<evaluate expression="pizzaFlowActions.saveOrder(order)" />
<transition to="thankYou" />
</action-state>
<action-state>元素一般都会有一个<evaluate>作为子元素。<evaluate>元素给出了行为状态要做的事情。expression属性指定了进入这个状态时要评估的表达式。在本示例中,给出的expression是SpEL表达式,它表明将会找到ID为pizzaFlowActions的bean并调用其saveOrder()方法。
Spring Web Flow与表达式语言:SpEL是默认和推荐使用的表达式语言。
决策状态
决策状态能够在流程执行时产生两个分支。决策状态将评估一个Boolean类型的表达式,然后在两个状态转移中选择一个,这要取决于表达式会计算出true还是false。在XML流程定义中,决策状态通过<decision-state>元素进行定义。
<decision-state id="checkDeliverArea">
<if test="pizzaFlowActions.checkDeliverArea(customer.zipCode)"
then="addCustomer"
else ="deliveryWarning"/>
</decision-state>
<decision-state>并不是独立完成工作的。<if>元素是决策状态的核心。这是表达式进行评估的地方,如果表达式结果为true,流程将转移到then属性指定的状态中,如果结果为false,流程将会转移到else属性指定的状态中。
子流程状态
将流程分成独立的部分是个不错的主意。<subflow-state>允许在一个正在执行的流程中调用另一个流程。
<!-- Order -->
<subflow-state id="order" subflow="pizza/order">
<input name="order" value="order"/>
<transition on="orderCreated" to="payment" />
</subflow-state>
<input>元素用于传递订单对象作为子流程的输入。如果子流程结束的<end-state>状态ID为orderCreated,那么流程将会转移到名为payment的状态。
结束状态
所有的流程都要结束。这就是当流程转移到结束状态时所做的。
<end-state id="customerReady" />
当到达<end-state>状态,流程会结束。接下来会发生什么取决于几个因素:
- 如果结束的流程是一个子流程,那调用它的流程将会从<subflow-state>处继续执行。<end-state>的ID将会用作事件触发从<subflow-state>开始的转移。
- 如果<end-state>设置了view属性,指定的视图将会被渲染。视图可以是相对于流程路径的视图模板,如果添加“externalRedirect:”前缀的话,将会重定向到流程外部的页面,如果添加“flowRedirect:”将重定向到另一个流程中。
- 如果结束的流程不是子流程,也没有指定view属性,那这个流程只是会结束而已。浏览器最后将会加载流程的基本URL地址,当前已没有活动的流程,所以会开始一个新的流程实例
需要意识到流程可能会有不止一个结束状态。子流程的结束状态ID确定了激活的事件,所以你可能会希望通过多种结束状态来结束子流程,从而能够在调用流程中触发不同的事件。即使不是在子流程中,也有可能在结束流程后,根据流程的执行情况有多个显示页面供选择。
转移
转移连接了流程中的状态。流程中除结束状态之外的每个状态,至少都需要一个转移,这样就能够知道一旦这个状态完成时流程要去向哪里。状态可以有多个转移,分别对应于当前状态结束时可以执行的不同的路径。
转移使用<transition>元素来进行定义,它会作为各种状态元素(<action-state>、<view-state>、<subflow-state>)的子元素。最简单的形式就是<transition>元素在流程中指定下一个状态:
<transition to="customerReady" />
属性to用于指定流程的下一个状态。如果<transition>只使用了to属性,那这个转移就会是当前状态的默认转移选项,如果没有其他可用转移的话,就会使用它。
更常见的转移定义是基于事件的触发来进行的。在视图状态,事件通常会是用户采取的动作。在行为状态,事件是评估表达式得到的结果。而在子流程状态,事件取决于子流程结束状态的ID。在任意的事件中,你可以使用on属性来指定触发转移的事件:
<transition on="phoneEntered" to="lookupCustomer"/>
如果触发了phoneEntered事件,流程将会进入lookupCustomer状态。而在抛出异常后,流程也可以进入到另一个状态。
<transition on-exception="com.springinaction.pizza.service.CustomerNotFoundException"
to="registrationForm" />
属性on-exception与属性on类似,它是指定了要发生转移的异常而不是一个事件。
全局转移
创建完流程后会发现有一些状态使用了通用的转移。而与其在多个状态重复使用,不如将这个转移定义为全局转移。例如:
<global-transitions>
<transition on="cancel" to="endState" />
</global-transitions>
定义完全局转移,流程中所有的状态都会默认拥有这个cancel转移。
流程数据
当流程从一个状态到达另一个状态时,它会带走一些数据。有时这些数据很快就会被使用,比如直接展示给用户,有时这些数据需要在整个流程中传递并在流程结束时使用。
声明变量
流程数据是保存在变量中的,而变量可以在流程的任意位置进行引用,并且可以以多种方式进行创建。其中最简单的方式就是使用<var>元素:
<var name="customer" class="com.springinaction.pizza.domain.Customer"/>
这里创建了一个新的Customer实例并将其放在customer变量中,这个变量可以在流程的任意状态下进行访问使用。
类似的,<set>元素也可以设置变量的值:
<set name="flowScope.pizza"
value="new com.springinaction.pizza.domain.Pizza()" />
定义流程数据的作用域
流程数据会拥有不同的生命作用域和可见性,Spring Web Flow定义了五种不同作用域。
| 范围 | 生命周期 |
|---|---|
| Conversation | 最高层级的流程开始时创建,在最高层级的流程结束时销毁。由最高层级的流程和其所有的子流程所共享 |
| Flow | 当流程开始时创建,在流程结束时销毁。只在创建它的流程中是可见的 |
| Request | 当一个请求进入流程时创建,流程返回时销毁 |
| Flash | 流程开始时创建,流程结束时销毁。在视图状态解析后,才会被清除 |
| View | 进入视图状态时创建,退出这个状态时销毁,只在视图状态内可见 |
当使用<var>元素声明变量时,变量始终是流程作用域的,也就是在流程作用域内定义变量。当使用<set>或<evaluate>时,作用域通过name或result属性的前缀指定。例如,将一个值赋给流程作用域的theAnswer变量:
<set name="flowScope.theAnswer" value="42"/>
组合起来:披萨流程
定义基本流程
图中方框代表了状态而箭头代表了转移。以下程序定义了实现披萨订单的整体流程。
<?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-2.0.xsd">
<var name="order" class="com.springinaction.pizza.domain.Order"/> 每次的流程的开始都是有一个新的订单
<!-- Customer -->
<subflow-state id="identifyCustomer" subflow="pizza/customer"> 子流程状态。 调用customer子流程,完成对顾客订单的
<output name="customer" value="order.customer"/> 使用customer子流程中customer类型填充order中的customer属性
<transition on="customerReady" to="bulidOrder" /> 当customer子流程的结束状态是customerReady时,转移到bulidOrder子流程
</subflow-state>
<!-- Order -->
<subflow-state id="buildOrder" subflow="pizza/order">
<input name="order" value="order"/> 通过input使得主流程中的order来填充子流程中的order
<transition on="orderCreated" to="takePayment" />
</subflow-state>
<!-- Payment -->
<subflow-state id="takePayment" subflow="pizza/payment">
<input name="order" value="order"/>
<transition on="paymentTaken" to="saveOrder"/>
</subflow-state>
<action-state id="saveOrder">
<evaluate expression="pizzaFlowActions.saveOrder(order)" />
<transition to="thankCustomer" />
</action-state>
<view-state id="thankCustomer"> 简单视图,“/WEB-INF/flows/pizza/thankCustomer.jsp”
<transition to="endState" />
</view-state>
<!-- End state -->
<end-state id="endState" />
<global-transitions>
<transition on="cancel" to="endState" />
</global-transitions>
</flow>
每次流程开始时都会创建一个Order实例。order类会带有关于订单的所有信息。
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable("order")
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
private Customer customer;
private List<Pizza> pizzas;
private Payment payment;
public Order() {
pizzas = new ArrayList<Pizza>();
customer = new Customer();
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public List<Pizza> getPizzas() {
return pizzas;
}
public void setPizzas(List<Pizza> pizzas) {
this.pizzas = pizzas;
}
public void addPizza(Pizza pizza) {
pizzas.add(pizza);
}
public float getTotal() {
return 0.0f;//pricingEngine.calculateOrderTotal(this);
}
public Payment getPayment() {
return payment;
}
public void setPayment(Payment payment) {
this.payment = payment;
}
}
流程定义的主要组成部分是流程的状态,默认情况下,流程定义文件中的第一个状态会是流程访问的第一个状态。本例中就是identifyCustomer状态(一个子流程)。也可以通过<flow>元素的start-state属性来指定任意状态为开始状态:
<?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-2.3.xsd"
start-state="identifyCustomer">
...
</flow>
流程变量order将在前3个状态中进行填充并在第4个状态中进行保存。identifyCustomer子流程使用了<output>元素来填充order的customer属性,将其设置为调用顾客子流程收到的输出。buildOrder和takePayment状态使用了不同的方式,它们使用<input>将order流程变量作为输入,这些子流程就能在其内部填充order对象。
在订单得到顾客、披萨以及支付信息后,就可以对其进行保存。saveOrder是处理这个任务的行为状态。它使用<evaluate>来调用ID为pizzaFlowActions的Bean的saveOrder()方法,并将保存的订单对象传递进来。订单完成保存后会转移到thankCustomer。
感谢顾客订购的JSP视图
<html xmlns:jsp="http://java.sun.com/JSP/Page">
<jsp:output omit-xml-declaration="yes" />
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<head><title>Spizza</title></head>
<body>
<h2>Thank you for your order!</h2>
<![CDATA[
<a href='${flowExecutionUrl}&_eventId=finished'>Finish</a>
]]>
</body>
</html>
收集顾客信息
使用Web流程来识别界的披萨顾客
<?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-2.0.xsd">
<input name="order" required="true" />
<!-- Customer -->
<view-state id="welcome">
<transition on="phoneEntered" to="lookupCustomer" />
<transition on="cancel" to="cancel" />
</view-state>
<action-state id="lookupCustomer">
<evaluate result="order.customer"
expression="pizzaFlowActions.lookupCustomer(requestParameters.phoneNumber)" />
<transition to="registrationForm"
on-exception="com.springinaction.pizza.service.CustomerNotFoundException" />
<transition to="customerReady" />
</action-state>
<view-state id="registrationForm" model="order" popup="true">
<on-entry>
<evaluate
expression="order.customer.phoneNumber = requestParameters.phoneNumber" />
</on-entry>
<transition on="submit" to="checkDeliveryArea" />
<transition on="cancel" to="cancel" />
</view-state>
<decision-state id="checkDeliveryArea">
<if test="pizzaFlowActions.checkDeliveryArea(order.customer.zipCode)"
then="addCustomer" else="deliveryWarning" />
</decision-state>
<view-state id="deliveryWarning">
<transition on="accept" to="addCustomer" />
<transition on="cancel" to="cancel" />
</view-state>
<action-state id="addCustomer">
<evaluate expression="pizzaFlowActions.addCustomer(order.customer)" />
<transition to="customerReady" />
</action-state>
<!-- End state -->
<end-state id="cancel" />
<end-state id="customerReady" />
</flow>
下面将这个流程分解:
询问电话号码
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<title>Spring Pizza</title>
</head>
<body>
<h2>Welcome to Spring Pizza!!!</h2>
<form:form>
<input type="hidden" name="_flowExecutionKey"
value="${flowExecutionKey}" />
<input type="text" name="phoneNumber" />
<br />
<input type="submit" name="_eventId_phoneEntered"
value="Lookup Customer" />
</form:form>
</body>
</html>
这个简单的表单用来让用户输入电话号码,有两个特殊的部分,首先是隐藏的_flowExecutionKey输入。当进入视图状态时,流程暂停并等待用户采取一些行为。当用户提交表单时,流程执行键会在_flowExecutionKey输入域中返回,并在流程暂停的位置进行恢复。
还需要注意提交按钮的名称_eventId_部分是Spring Web Flow的一个线索,它表明了接下来要触发事件。当点击这个按钮提交表单时,就会触发phoneEntered事件,进而转移到lookupCustomer。
查找顾客
当欢迎顾客的表单提交后,顾客的电话号码将包含在请求参数中,并用于查询顾客。lookupCustomer状态的<evaluate>元素是查找发生的位置。它将电话号码从请求参数中抽取出来,并传递到pizzaFlowActions Bean的lookupCustomer()方法中。该方法要么返回Customer对象,要么抛出CustomerNotFoundException异常。
在前一种情况下,Customer对象会被设置到customer变量中(通过result属性)并默认的转移将流程带到customerReady状态。如果没有查到顾客,那么会抛出异常,流程会转移到registrationForm状态。
注册新顾客
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head><title>Spring Pizza</title></head>
<body>
<h2>Customer Registration</h2>
<form:form commandName="order">
<input type="hidden" name="_flowExecutionKey"
value="${flowExecutionKey}"/>
<b>Phone number: </b><form:input path="customer.phoneNumber"/><br/>
<b>Name: </b><form:input path="customer.name"/><br/>
<b>Address: </b><form:input path="customer.address"/><br/>
<b>City: </b><form:input path="customer.city"/><br/>
<b>State: </b><form:input path="customer.state"/><br/>
<b>Zip Code: </b><form:input path="customer.zipCode"/><br/>
<input type="submit" name="_eventId_submit"
value="Submit" />
<input type="submit" name="_eventId_cancel"
value="Cancel" />
</form:form>
</body>
</html>
检查配送区域
%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head><title>Spring Pizza</title></head>
<body>
<h2>Delivery Unavailable</h2>
<p>The address is outside of our delivery area. The order
may still be taken for carry-out.</p>
<a href="${flowExecutionUrl}&_eventId=accept">Accept</a> |
<a href="${flowExecutionUrl}&_eventId=cancel">Cancel</a>
</body>
</html>
存储顾客数据
addCustomer有一个<evaluate>元素,它会调用pizzaFlowActions.addCustomer()方法,将order.customer流程参数传递进去。
一旦这个流程完成,就会执行默认转移,流程会转移到ID为customerReady的结束状态。
结束流程
当customer流程完成所有的路径后,会到达customerReady的结束状态。当调用它的披萨流程恢复时,它会接收到一个customerReady事件,这个事件将使得流程转移到buildOrder状态。
注意,customerReady结束状态包含了一个<output>元素。在流程中,它等同于Java的return语句。它会从子流程中传递一些数据到调用流程。例如,<output>元素返回customer变量,这样披萨流程中的identifyCustomer子流程状态就可以将其指定给订单。
另外,如果用户在任意地方触发了cancel事件,将会通过cancel状态结束流程,这也会在披萨流程中触发cancel事件并导致转移到披萨流程的结束状态。
构建订单
<?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-2.0.xsd">
<input name="order" required="true" />
<!-- Order -->
<view-state id="showOrder">
<transition on="createPizza" to="createPizza" />
<transition on="checkout" to="orderCreated" />
<transition on="cancel" to="cancel" />
</view-state>
<view-state id="createPizza" model="flowScope.pizza">
<on-entry>
<set name="flowScope.pizza" value="new com.springinaction.pizza.domain.Pizza()" />
<evaluate result="viewScope.toppingsList"
expression="T(com.springinaction.pizza.domain.Topping).asList()" />
</on-entry>
<transition on="addPizza" to="showOrder">
<evaluate expression="order.addPizza(flowScope.pizza)" />
</transition>
<transition on="cancel" to="showOrder" />
</view-state>
<!-- End state -->
<end-state id="cancel" />
<end-state id="orderCreated" />
</flow>
接下来会看到showOrder状态,它是一个基本的视图状态,具有3个不同的转移,分别用于创建披萨、提交订单和取消订单。
createPizza的视图是一个表单,这个表单可以添加新的Pizza对象到订单。
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<div>
<h2>Create Pizza</h2>
<form:form commandName="pizza">
<input type="hidden" name="_flowExecutionKey"
value="${flowExecutionKey}"/>
<b>Size: </b><br/>
<form:radiobutton path="size" label="Small (12-inch)" value="SMALL"/><br/>
<form:radiobutton path="size" label="Medium (14-inch)" value="MEDIUM"/><br/>
<form:radiobutton path="size" label="Large (16-inch)" value="LARGE"/><br/>
<form:radiobutton path="size" label="Ginormous (20-inch)" value="GINORMOUS"/><br/>
<br/>
<b>Toppings: </b><br/>
<form:checkboxes path="toppings" items="${toppingsList}"
delimiter="<br/>"/><br/><br/>
<input type="submit" class="button"
name="_eventId_addPizza" value="Continue"/>
<input type="submit" class="button"
name="_eventId_cancel" value="Cancel"/>
</form:form>
</div>
有两种方法可以结束流程,用户可以点击showOrder视图中的Cancel按钮或者Checkout按钮。这两种操作都会使流程转移到一个
支付
在披萨流程要结束的时候,最后的子流程提示用户输入他们的支付信息。
<?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-2.0.xsd">
<input name="order" required="true"/>
<view-state id="takePayment" model="flowScope.paymentDetails">
<on-entry>
<set name="flowScope.paymentDetails"
value="new com.springinaction.pizza.domain.PaymentDetails()" />
<evaluate result="viewScope.paymentTypeList"
expression="T(com.springinaction.pizza.domain.PaymentType).asList()" />
</on-entry>
<transition on="paymentSubmitted" to="verifyPayment" />
<transition on="cancel" to="cancel" />
</view-state>
<action-state id="verifyPayment">
<evaluate result="order.payment" expression=
"pizzaFlowActions.verifyPayment(flowScope.paymentDetails)" />
<transition to="paymentTaken" />
</action-state>
<!-- End state -->
<end-state id="cancel" />
<end-state id="paymentTaken" />
</flow>
在流程进入takePayment视图时,
package com.springinaction.pizza.domain;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.text.WordUtils;
public enum PaymentType {
CASH, CHECK, CREDIT_CARD;
public static List<PaymentType> asList() {
PaymentType[] all = PaymentType.values();
return Arrays.asList(all);
}
@Override
public String toString() {
return WordUtils.capitalizeFully(name().replace('_', ' '));
}
}
在面对支付表单的时候,用户可能提交支付,也可能会取消。根据做出的选择,支付子流程将名为paymentTaken或cancel的
保护Web流程
Spring Web Flow中的状态、转移甚至整个流程都可以借助
<view-state id="restricted">
<secured attributes="ROLE_ADMIN" match="all"/>
</view-state>
按照这里的配置,只有授权ROLE_ADMIN访问权限(借助attributes属性)的用户才能访问这个视图状态。attributes属性使用逗号分隔的权限列表来表明用户要访问指定状态、转移或流程所需要的权限。match属性可以设置为any或all。如果是any,那么用户至上具备一个attributes属性所列的权限。如果的all,那么用户必须具有所有权限。具体会在下一章介绍。
浙公网安备 33010602011771号