笔记39 Spring Web Flow——订单流程(收集顾客信息)
如果你曾经订购过披萨,你可能会知道流程。他们首先会询问你的电 话号码。电话号码除了能够让送货司机在找不到你家的时候打电话给 你,还可以作为你在这个披萨店的标识。如果你是回头客,他们可以 使用这个电话号码来查找你的地址,这样他们就知道将你的订单派送 到什么地方了。
对于一个新的顾客来讲,查询电话号码不会有什么结果。所以接下 来,他们将询问你的地址。这样,披萨店的人就会知道你是谁以及将 披萨送到哪里。但是在问你要哪种披萨之前,他们要确认你的地址在 他们的配送范围之内。如果不在的话,你需要自己到店里并取走披 萨。
在每个披萨订单开始前的提问和回答阶段可以用下图的流程图来表示。
这个流程不是线性的而是在好几个地方根据不同的条件有了分支。例如,在查找顾客后,流程可能结束(如果找到了顾客),也有可能转移到注册表单(如果没有找到 顾客)。同样,在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——订单流程(收集顾客信息)的更多相关文章
- 笔记38 Spring Web Flow——订单流程(定义基本流程)
做一个在线的披萨订购应用 实际上,订购披萨的过程可以很好地定义在一个流程中.我们首先从 构建一个高层次的流程开始,它定义了订购披萨的整体过程.接下 来,我们会将这个流程拆分成子流程,这些子流程在较低的 ...
- 笔记40 Spring Web Flow——订单流程(构建订单)
二.订单子流程 在识别完顾客之后,主流程的下一件事情就是确定他们想要什么类型 的披萨.订单子流程就是用于提示用户创建披萨并将其放入订单中 的,如下图所示. showOrder状态位于订单子流程的中心位 ...
- Spring实战第八章学习笔记————使用Spring Web Flow
Spring实战第八章学习笔记----使用Spring Web Flow Spring Web Flow是一个Web框架,它适用于元素按规定流程运行的程序. 其实我们可以使用任何WEB框架写流程化的应 ...
- 笔记37 Spring Web Flow——流程的组件
在Spring Web Flow中,流程是由三个主要元素定义的:状态.转移和 流程数据. 一.状态 Spring Web Flow定义了五种不同类型的状态.通过选择Spring Web Flow的状态 ...
- 笔记42 Spring Web Flow——Demo(2)
转自:https://www.cnblogs.com/lyj-gyq/p/9117339.html 为了更好的理解披萨订购应用,再做一个小的Demo. 一.Spring Web Flow 2.0新特性 ...
- 笔记43 Spring Web Flow——订购披萨应用详解
一.项目的目录结构 二.订购流程总体设计 三.订购流程的详细设计 1.定义基本流程pizza-flow.xml <?xml version="1.0" encoding=&q ...
- 笔记36 Spring Web Flow——配置
Spring Web Flow是一个Web框架,它适用于元素按规定流程运行的程序.Spring Web Flow是Spring MVC的扩展,它支持开发基于流程的应用程 序.它将流程的定义与实现流程行 ...
- 笔记41 Spring Web Flow——Demo
订购披萨的应用整体比较比较复杂,现拿出其中一个简化版的流程:即用户访问首页,然后输入电话号(假定未注册)后跳转到注册页面,注册完成后跳转到配送区域检查页面,最后再跳转回首页.通过这个简单的Demo用来 ...
- Spring Web Flow 入门demo(三)嵌套流程与业务结合 附源代码
上篇博客我们说Spring web Flow与业务结合的方式主要有三种,以下我们主要介绍一下第三种的应用方式 3,运行到<action-state> 元素 SpringWeb Flow 中 ...
随机推荐
- spring 注入bean的两种方式
我们都知道,使用spring框架时,不用再使用new来实例化对象了,直接可以通过spring容器来注入即可. 而注入bean有两种方式: 一种是通过XML来配置的,分别有属性注入.构造函数注入和工厂方 ...
- SpringMVC学习(6):数据验证
在系列(4).(5)中我们展示了如何绑定数据,绑定完数据之后如何确保我们得到的数据的正确性?这就是我们本篇要说的内容 -> 数据验证. 这里我们采用Hibernate-validator来进行验 ...
- spring纯注解的account案例
dao层: package cn.mepu.dao.imp; import cn.mepu.dao.IAccountDao; import cn.mepu.domain.Account; import ...
- Android应用图标尺寸规范(转)
转自:http://blog.sina.com.cn/s/blog_4b20ae2e0101h84o.html Android Icon Size and Location for Apps DENS ...
- Windows 获取windows密码
#include <iostream> #define Main main #define COLOR_GREEN system("color 2"); #includ ...
- loadrunner自定义函数
https://zhangfy068.iteye.com/blog/1614794 Loadruner 有四种实现自定义函数的方式,根据脚本编写方便性进行选择不同的方式. (1)直接引用法: Acti ...
- 【NOI2019模拟2019.6.29】字符串(SA|SAM+主席树)
Description: 1<=n<=5e4 题解: 考虑\(f\)这个东西应该是怎样算的? 不妨建出SA,然后按height从大到小启发式合并,显然只有相邻的才可能成为最优答案.这样的只 ...
- 【NOIP2019模拟2019.9.4】B(期望的线性性)
题目描述: \(1<=n,ai<=5*10^5\) 题解: 我是弱智我不会期望线性. 设\(E(a[i])\)表示第i个期望被减的个数. \(E(a[1])=a[1]\) 不难发现\(E( ...
- 基础课(三)实验串入OSPF协议和HSRP协议以及HSRP外部链路跟踪
实验要求1: ,2,3,4分别是vlan10,20,30,40的网关(网关IP-192.168.X.254 /24) 对vlan10做HSRP热备 SW1做主网关,SW2做备份网关 ...
- C语言新手写扫雷攻略3
界面绘制好后,雷数也布置了,接下来就是游戏的运行过程了,今天先不说具体过程,再来看看需要用到的辅助函数 先是简单的画红旗,鼠标右键的功能是画红旗,至此我们都是在使用函数自己绘图,效率是低,但有助于理解 ...