如果你曾经订购过披萨,你可能会知道流程。他们首先会询问你的电 话号码。电话号码除了能够让送货司机在找不到你家的时候打电话给 你,还可以作为你在这个披萨店的标识。如果你是回头客,他们可以 使用这个电话号码来查找你的地址,这样他们就知道将你的订单派送 到什么地方了。

  对于一个新的顾客来讲,查询电话号码不会有什么结果。所以接下 来,他们将询问你的地址。这样,披萨店的人就会知道你是谁以及将 披萨送到哪里。但是在问你要哪种披萨之前,他们要确认你的地址在 他们的配送范围之内。如果不在的话,你需要自己到店里并取走披 萨。

  在每个披萨订单开始前的提问和回答阶段可以用下图的流程图来表示。

  这个流程不是线性的而是在好几个地方根据不同的条件有了分支。例如,在查找顾客后,流程可能结束(如果找到了顾客),也有可能转移到注册表单(如果没有找到 顾客)。同样,在checkDeliveryArea状态,顾客有可能会被警 告也有可能不被警告他们的地址在配送范围之外。

一、识别顾客的流程定义(customer-flow.xml)

 <?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>

这个流程包含了几个新的技巧,包括首次使用的<decision-state>元素。因为它是pizza流程的子流程,所以它也可以接受 Order对象作为输入。

1.询问电话号码

  welcome状态是一个很简单的视图状态,它欢迎访问Spizza站点的顾 客并要求他们输入电话号码。这个状态并没有什么特殊的。它有两个 转移:如果从视图触发phoneEntered事件的话,转移会将流程定 向到lookupCustomer,另外一个就是在全局转移中定义的用来响 应cancel事件的cancel转移。welcome状态的有趣之处在于视图本身。视图welcome定义在“/WEBINF/flows/ pizza/customer/welcome.jsp”中,如下所示。

 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>My JSP 'welcome.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
--> </head> <body>
<h2>Welcome to Spizza!!!</h2>
<form:form>
<!-- 流程执行的key -->
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"/> <input type="text" name="phoneNumber"/><br/>
<!-- 触发phoneEntered事件 -->
<input type="submit" name="_evenId_phoneEntered" value="Lookup Customer"/>
</form:form>
</body>
</html>

  这个简单的表单提示用户输入其电话号码。但是表单中有两个特殊的 部分来驱动流程继续。

  首先要注意的是隐藏的“_flowExecutionKey”输入域。当进入视图 状态时,流程暂停并等待用户采取一些行为。赋予视图的流程执行 key(flow execution key)就是一种返回流程的“回程票”(claim ticket)。当用户提交表单时,流程执行key会 在“_flowExecutionKey”输入域中返回并在流程暂停的位置进行恢 复。

  还要注意的是提交按钮的名字。按钮名字的“_eventId_”部分是提 供给Spring Web Flow的一个线索,它表明了接下来要触发事件。当点 击这个按钮提交表单时,会触发phoneEntered事件进而转移 到lookupCustomer。

2.查找顾客

  当欢迎表单提交后,顾客的电话号码将包含在请求参数中并准备用于 查询顾客。lookupCustomer状态的<evaluate>元素是查找发生的地方。它将电话号码从请求参数中抽取出来并传递 到pizzaFlowActions bean的lookupCustomer()方法中。 lookupCustomer()方法要么返回Customer对象,要么抛出CustomerNotFoundException异常。 在前一种情况下,Customer对象将会设置到customer变量中(通 过result属性)并且默认的转移将把流程带到customerReady状 态。但是如果不能找到顾客的话,将抛出 CustomerNotFoundException并且流程被转移 到registrationForm状态。

3.注册新顾客

  registrationForm状态是要求用户填写配送地址的。就像我们之 前看到的其他视图状态,它将被渲染成JSP。JSP文件如下所示。

 <%@ 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>

在这里不是通过请求参数一个个地处理输入域,而是以更好的方式将 表单绑定到Customer对象上——让框架来做所有繁杂的工作。

4.检查配送区域

  在顾客提供其地址后,我们需要确认他的住址在配送范围之内。如果 Spizza不能派送给他们,那么我们要让顾客知道并建议他们自己到店 面里取走披萨。为了做出这个判断,我们使用了决策状态。决策状 态checkDeliveryArea有一个<if>元素,它将顾客的邮政编码传 递到pizzaFlowActions bean的checkDeliveryArea()方法中。 这个方法将会返回一个Boolean值:如果顾客在配送区域内则 为true,否则为false。

  如果顾客在配送区域内的话,那流程转移到addCustomer状态。否 则,顾客被带入到deliveryWarning视图状 态。deliveryWarning背后的视图就是“/WEBINF/flows/pizza/customer/deliveryWarning.jspx”,如下所示:

 <%@ 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>

  在deliveryWarning.jsp中与流程相关的两个关键点就是那两个链接, 它们允许用户继续订单或者将其取消。通过使用与welcome状态相 同的flowExecurtionUrl变量,这些链接分别触发流程中的 accept或cancel事件。如果发送的是accept事件,那么流程会转 移到addCustomer状态。否则,接下来会是全局的取消转移,子流 程将会转移到cancel结束状态。

5.存储顾客数据

  当流程抵达addCustomer状态时,用户已经输入了他们的地址。为 了将来使用,这个地址需要以某种方式存储起来(可能会存储在数据 库中)。addCustomer状态有一个<evaluate>元素,它会调 用pizzaFlowActions bean的addCustomer()方法,并将customer流程参数传递进去。

  一旦这个过程完成,会执行默认的转移,流程将会转移到ID 为customerReady的结束状态。

6.结束流程

  这个流程中,它 不仅仅只有一个结束状态,而是两个。当子流程完成时,它会触发一 个与结束状态ID相同的流程事件。如果流程只有一个结束状态的话, 那么它始终会触发相同的事件。但是如果有两个或更多的结束状态, 流程能够影响到调用状态的执行方向。

  当customer流程走完所有正常的路径后,它最终会到达ID 为customerReady的结束状态。当调用它的披萨流程恢复时,它会 接收到一个customerReady事件,这个事件将使得流程转移 到buildOrder状态。

  要注意的是customerReady结束状态包含了一个<output>元素。 在流程中这个元素等同于Java中的return语句。它从子流程中传递 一些数据到调用流程。在本示例中,<output>元素返回customer 流程变量,这样在披萨流程中,就能够将identifyCustomer子流 程的状态指定给订单。另一方面,如果在识别顾客流程的任意地方触 发了cancel事件,将会通过ID为cancel的结束状态退出流程,这 也会在披萨流程中触发cancel事件并导致转移(通过全局转移)到 披萨流程的结束状态。

笔记39 Spring Web Flow——订单流程(收集顾客信息)的更多相关文章

  1. 笔记38 Spring Web Flow——订单流程(定义基本流程)

    做一个在线的披萨订购应用 实际上,订购披萨的过程可以很好地定义在一个流程中.我们首先从 构建一个高层次的流程开始,它定义了订购披萨的整体过程.接下 来,我们会将这个流程拆分成子流程,这些子流程在较低的 ...

  2. 笔记40 Spring Web Flow——订单流程(构建订单)

    二.订单子流程 在识别完顾客之后,主流程的下一件事情就是确定他们想要什么类型 的披萨.订单子流程就是用于提示用户创建披萨并将其放入订单中 的,如下图所示. showOrder状态位于订单子流程的中心位 ...

  3. Spring实战第八章学习笔记————使用Spring Web Flow

    Spring实战第八章学习笔记----使用Spring Web Flow Spring Web Flow是一个Web框架,它适用于元素按规定流程运行的程序. 其实我们可以使用任何WEB框架写流程化的应 ...

  4. 笔记37 Spring Web Flow——流程的组件

    在Spring Web Flow中,流程是由三个主要元素定义的:状态.转移和 流程数据. 一.状态 Spring Web Flow定义了五种不同类型的状态.通过选择Spring Web Flow的状态 ...

  5. 笔记42 Spring Web Flow——Demo(2)

    转自:https://www.cnblogs.com/lyj-gyq/p/9117339.html 为了更好的理解披萨订购应用,再做一个小的Demo. 一.Spring Web Flow 2.0新特性 ...

  6. 笔记43 Spring Web Flow——订购披萨应用详解

    一.项目的目录结构 二.订购流程总体设计 三.订购流程的详细设计 1.定义基本流程pizza-flow.xml <?xml version="1.0" encoding=&q ...

  7. 笔记36 Spring Web Flow——配置

    Spring Web Flow是一个Web框架,它适用于元素按规定流程运行的程序.Spring Web Flow是Spring MVC的扩展,它支持开发基于流程的应用程 序.它将流程的定义与实现流程行 ...

  8. 笔记41 Spring Web Flow——Demo

    订购披萨的应用整体比较比较复杂,现拿出其中一个简化版的流程:即用户访问首页,然后输入电话号(假定未注册)后跳转到注册页面,注册完成后跳转到配送区域检查页面,最后再跳转回首页.通过这个简单的Demo用来 ...

  9. Spring Web Flow 入门demo(三)嵌套流程与业务结合 附源代码

    上篇博客我们说Spring web Flow与业务结合的方式主要有三种,以下我们主要介绍一下第三种的应用方式 3,运行到<action-state> 元素 SpringWeb Flow 中 ...

随机推荐

  1. myeclipse中出现The method xxx of type must override or implement a supertype

    出现问题提示:The method xxx of type must override or implement a supertype? annotation:@Override的原因 查阅了一下资 ...

  2. 2018-8-10-win10-uwp-ping

    title author date CreateTime categories win10 uwp ping lindexi 2018-08-10 19:17:19 +0800 2018-2-13 1 ...

  3. WiFi基础知识

    自从只需少量的话费就可以将笔记本.平板电脑连接到互联网,WiFi已成为我们熟知的网络,并无处不在.Wi-Fi对于一些物联网应用十分有用,比如楼宇自动化.内部能源管理.WiFi的重要性对于我们的日常生活 ...

  4. 第2篇Kubernetes架构

      一.Kubernetes 架构: Kubernetes Cluster 由 Master 和 Node 组成,节点上运行着若干 Kubernetes 服务. Master 节点 Master 是 ...

  5. 深入理解Magento - 第六章 - 高级Magento模型

    我们讲过Magento有两种模型,简单模型和EAV(Entity Attribute Value)模型.上一章我们讲过所有的Magento模型都是继承自Mage_Core_Model_Abstract ...

  6. 深入理解Magento - 第五章 Magento资源配置

    对于任何一个更新频繁的项目来说,保持开发环境和生产环境的数据库同步是件很头疼的事情.Magento提供了一套系统,用版本化的资源迁移脚本来解决这个问题. 上一章,我们为 Helloworld Blog ...

  7. Yii2 自定义组件

    basic\components\HelloWidget namespace app\components; use yii\base\Widget; use yii\helpers\Html; cl ...

  8. go语言中使用正则表达式

    一.代码 package main import ( "fmt" "regexp" ) func main() { text := `Hello 世界!123 ...

  9. NX二次开发-Block UI C++界面Enumeration(枚举)控件的获取(持续补充)

    NX9+VS2012 public: void SetBlockUIShow(); void EnumInt::SetBlockUIShow() { //获取枚举控件 PropertyList* En ...

  10. 前台页面中json和字符串相互转化

    比如我有两个变量,我要将a转换成字符串,将b转换成JSON对象: var a={"name":"tom","sex":"男&quo ...