一个新的披萨连锁店 Spizza 决定允许用户在线订购以减轻店面电话的压力。当顾客访问 Spizza 站点时,他们需要进行用户识别,选择一个或更多披萨添加到订单中,提供支付信息然后提交订单并等待热乎又新鲜的披萨送过来。图 8.2 阐述了这个流程。
图中的方框代表了状态而箭头代表了转移。你可以看到,订购披萨的整个流程很简单且是线性的。在 Spring Web Flow中,表示这个流程是很容易的。使这个过程变得更有意思的就是前三个流程会比图中的简单方框更复杂。
以下的程序清单 8.1 展示了如何使用 Spring Web Flow 的 XML 流程定义来实现披萨订单的整体流程。
程序清单 8.1 披萨订单流程定义为 Spring Web Flow
Copy <?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">
<var name="order" class="com.springinaction.pizza.domain.Order"/>
<subflow-state id="identifyCustomer" subflow="pizza/customer">
<output name="customer" value="order.customer"/>
<transition on="customerReady" to="buildOrder" />
</subflow-state>
<subflow-state id="buildOrder" subflow="pizza/order">
<input name="order" value="order"/>
<transition on="orderCreated" to="takePayment" />
</subflow-state>
<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">
<transition to="endState" />
</view-state>
<end-state id="endState" />
<global-transitions>
<transition on="cancel" to="endState" />
</global-transitions>
</flow>
在流程定义中,我们看到的第一件事就是 order 变量的声明。每次流程开始的时候,都会创建一个 Order 实例。Order 类会带有关于订单的所有信息,包含顾客信息、订购的披萨列表以及支付详情,如下面所示。
程序清单 8.2 Order 带有披萨订单的所有细节信息
Copy package com . springinaction . pizza . domain ;
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 Customer getCustomer () {
return 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 属性将任意状态指定为开始状态。
Copy <?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>
识别顾客、构造披萨订单以及支付这样的活动太复杂了,并不适合将其强行塞入一个状态。这是我们为何在后面将其单独定义为流程的原因。但是为了更好地整体了解披萨流程,这些活动都是以 <subflow-state>
元素来进行展现的。
流程变量 order 将在前三个状态中进行填充并在第四个状态中进行保存。identifyCustomer 子流程状态使用了元素来填充 order 的 customer 属性,将其设置为顾客子流程收到的输出。buildOrder 和 takePayment 状态使用了不同的方式,它们使用 <input> 将 order 流程变量作为输入,这些子流程就能在其内部填充 order 对象。
在订单得到顾客、一些披萨以及支付细节后,就可以对其进行保存了。saveOrder 是处理这个任务的行为状态。它使用来调用 ID 为 pizzaFlow-Actions 的 bean 的 saveOrder() 方法,并将保存的订单对象传递进来。订单完成保存后,它会转移到 thankCustomer。
thankCustomer 状态是一个简单的视图状态,后台使用了 /WEB-INF/flows/pizza/thankCustomer.jsp
这个 JSP 文件,如下所示:
Copy <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>
在“感谢”页面中,会感谢顾客的订购并为其提供一个完成流程的链接。这个链接是整个页面中最有意思的事情,因为它展示了用户与流程交互的唯一办法。
Spring Web Flow 为视图的用户提供了一个 flowExecutionUrl 变量,它包含了流程的 URL。结束链接将一个 _eventId
参数关联到 URL 上,以便回到 Web 流程时触发 finished 事件。这个事件将会让流程到达结束状态。
流程将会在结束状态完成。鉴于在流程结束后没有下一步做什么的具体信息,流程将会重新从 identifyCustomer 状态开始,以准备接受另一个披萨订单。
这涵盖了订购披萨的整体流程。但是这个流程并不仅仅是我们在代码清单 8.1 中所看到的这些。我们还需要定义 identifyCustomer、buildOrder、take-Payment 这些状态的子流程。让我们从识别用户开始构建这些流程。