- 作者简介
- 内容提要
- 关于本书
- 路线图
- 代码规范与下载
- 作者在线
- 封面插图简介
- 前言
- 译者序
- 致谢
- 第1部分 Spring 的核心
- 第1章 Spring 之旅
- 第2章 装配 Bean
- 第3章 高级装配
- 第4章 面向切面的 Spring
- 第2部分 Web 中的 Spring
- 第5章 构建 Spring Web 应用程序
- 第6章 渲染 Web 视图
- 第7章 Spring MVC 的高级技术
- 第8章 使用 Spring Web Flow
- 第9章 保护 Web 应用
- 第3部分 后端中的 Spring
- 第10章 通过 Spring 和 JDBC 征服数据库
- 第11章 使用对象-关系映射持久化数据
- 第12章 使用 NoSQL 数据库
- 第13章 缓存数据
- 第14章 保护方法应用
- 第4部分 Spring 集成
- 第15章 使用远程服务
- 第16章 使用 Spring MVC 创建 REST API
- 第17章 Spring消息
- 第18章 使用 WebSocket 和 STOMP 实现消息功能
- 第19章 使用 Spring 发送 Email
- 第20章 使用 JMX 管理 Spring Bean
- 第21章 借助 Spring Boot 简化 Spring 开发
8.3.2 收集顾客信息
如果你曾经订购过披萨,你可能会知道流程。他们首先会询问你的电话号码。电话号码除了能够让送货司机在找不到你家的时候打电话给你,还可以作为你在这个披萨店的标识。如果你是回头客,他们可以使用这个电话号码来查找你的地址,这样他们就知道将你的订单派送到什么地方了。
对于一个新的顾客来讲,查询电话号码不会有什么结果。所以接下来,他们将询问你的地址。这样,披萨店的人就会知道你是谁以及将披萨送到哪里。但是在问你要哪种披萨之前,他们要确认你的地址在他们的配送范围之内。如果不在的话,你需要自己到店里并取走披萨。
在每个披萨订单开始前的提问和回答阶段可以用图8.3的流程图来表示。
这个流程比整体的披萨流程更有意思。这个流程不是线性的而是在好几个地方根据不同的条件有了分支。例如,在查找顾客后,流程可能结束(如果找到了顾客),也有可能转移到注册表单(如果没有找到顾客)。同样,在checkDeliveryArea状态,顾客有可能会被警告也有可能不被警告他们的地址在配送范围之外。
图8.3 识别顾客的流程比披萨流程有了更多的分支
以下的程序清单展示了识别顾客的流程定义。
程序清单8.4 使用Web流程来识别饥饿的披萨顾客
这个流程包含了几个新的技巧,包括我们首次使用的<decision-state>元素。因为它是pizza流程的子流程,所以它也可以接受Order对象作为输入。
与前面一样,我们还是将这个流程的定义分解成一个个的状态,让我们从welcome状态开始。
询问电话号码
welcome状态是一个很简单的视图状态,它欢迎访问Spizza站点的顾客并要求他们输入电话号码。这个状态并没有什么特殊的。它有两个转移:如果从视图触发phoneEntered事件的话,转移会将流程定向到lookupCustomer,另外一个就是在全局转移中定义的用来响应cancel事件的cancel转移。
welcome状态的有趣之处在于视图本身。视图welcome定义在“/WEB-INF/flows/ pizza/customer/welcome.jspx”中,如下所示。
程序清单8.5 欢迎用户并询问他们的电话号码
这个简单的表单提示用户输入其电话号码。但是表单中有两个特殊的部分来驱动流程继续。
首先要注意的是隐藏的“_flowExecutionKey”输入域。当进入视图状态时,流程暂停并等待用户采取一些行为。赋予视图的流程执行key(flow execution key)就是一种返回流程的“回程票”(claim ticket)。当用户提交表单时,流程执行key会在“_flowExecutionKey”输入域中返回并在流程暂停的位置进行恢复。
还要注意的是提交按钮的名字。按钮名字的“_eventId_”部分是提供给Spring Web Flow的一个线索,它表明了接下来要触发事件。当点击这个按钮提交表单时,会触发phoneEntered事件进而转移到lookupCustomer。
查找顾客
当欢迎表单提交后,顾客的电话号码将包含在请求参数中并准备用于查询顾客。lookupCustomer状态的<evaluate>元素是查找发生的地方。它将电话号码从请求参数中抽取出来并传递到pizzaFlowActions bean的lookupCustomer()方法中。
目前,lookupCustomer()的实现并不重要。只需知道它要么返回Customer对象,要么抛出CustomerNotFoundException异常。
在前一种情况下,Customer对象将会设置到customer变量中(通过result属性)并且默认的转移将把流程带到customerReady状态。但是如果不能找到顾客的话,将抛出CustomerNotFoundException并且流程被转移到registrationForm状态。
注册新顾客
registrationForm状态是要求用户填写配送地址的。就像我们之前看到的其他视图状态,它将被渲染成JSP。JSP文件如下所示。
程序清单8.6 注册新顾客
这并非我们在流程中看到的第一个表单。welcome视图状态也为顾客展现了一个表单,那个表单很简单,并且只有一个输入域,从请求参数中获得输入域的值也很简单。但是注册表单就比较复杂了。
在这里不是通过请求参数一个个地处理输入域,而是以更好的方式将表单绑定到Customer对象上——让框架来做所有繁杂的工作。
检查配送区域
在顾客提供其地址后,我们需要确认他的住址在配送范围之内。如果Spizza不能派送给他们,那么我们要让顾客知道并建议他们自己到店面里取走披萨。
为了做出这个判断,我们使用了决策状态。决策状态checkDeliveryArea有一个<if>元素,它将顾客的邮政编码传递到pizzaFlowActions bean的checkDeliveryArea()方法中。这个方法将会返回一个Boolean值:如果顾客在配送区域内则为true,否则为false。
如果顾客在配送区域内的话,那流程转移到addCustomer状态。否则,顾客被带入到deliveryWarning视图状态。deliveryWarning背后的视图就是“/WEB-INF/flows/pizza/customer/deliveryWarning.jspx”,如下所示:
程序清单8.7 告知顾客不能将披萨配送到他们的地址
在deliveryWarning.jspx中与流程相关的两个关键点就是那两个链接,它们允许用户继续订单或者将其取消。通过使用与welcome状态相同的flowExecurtionUrl变量,这些链接分别触发流程中的accept或cancel事件。如果发送的是accept事件,那么流程会转移到addCustomer状态。否则,接下来会是全局的取消转移,子流程将会转移到cancel结束状态。
稍后我们将介绍结束状态。让我们先来看看addCustomer状态。
存储顾客数据
当流程抵达addCustomer状态时,用户已经输入了他们的地址。为了将来使用,这个地址需要以某种方式存储起来(可能会存储在数据库中)。addCustomer状态有一个<evaluate>元素,它会调用pizzaFlowActions bean的addCustomer()方法,并将customer流程参数传递进去。
一旦这个过程完成,会执行默认的转移,流程将会转移到ID为customerReady的结束状态。
结束流程
一般来讲,流程的结束状态并不会那么有意思。但是这个流程中,它不仅仅只有一个结束状态,而是两个。当子流程完成时,它会触发一个与结束状态ID相同的流程事件。如果流程只有一个结束状态的话,那么它始终会触发相同的事件。但是如果有两个或更多的结束状态,流程能够影响到调用状态的执行方向。
当customer流程走完所有正常的路径后,它最终会到达ID为customerReady的结束状态。当调用它的披萨流程恢复时,它会接收到一个customerReady事件,这个事件将使得流程转移到buildOrder状态。
要注意的是customerReady结束状态包含了一个<output>元素。在流程中这个元素等同于Java中的return语句。它从子流程中传递一些数据到调用流程。在本示例中,<output>元素返回customer流程变量,这样在披萨流程中,就能够将identifyCustomer子流程的状态指定给订单。另一方面,如果在识别顾客流程的任意地方触发了cancel事件,将会通过ID为cancel的结束状态退出流程,这也会在披萨流程中触发cancel事件并导致转移(通过全局转移)到披萨流程的结束状态。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论