国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > php框架 > 框架设计 > 《Spring实战》学习笔记-第八章:使用Spring Web Flow

《Spring实战》学习笔记-第八章:使用Spring Web Flow

来源:程序员人生   发布时间:2016-06-22 08:55:44 阅读次数:3622次

第4版的第8章内容与第3版基本1致。

本章内容:

  • 创建会话式web利用程序
  • 定义流程状态和行动
  • 保护web流程

互联网的1个奇特的地方就在于它很容易让人迷失。有如此多的内容可以查看和浏览,而超链接是其强大魔力的核心所在。

有时候,web利用程序需要控制web冲浪者的导向,引导他们1步步地访问利用。比如电子商务网站的付款流程,从购物车开始,利用程序会引导你顺次经过配送详情、账单信息和终究的定单确认。

Spring Web Flow是1个web框架,它适用于元素规定流程运行的程序。本章中,我们将会探索它是如何用于Spring Web框架平台的。

其实我们可使用任何的Web框架编写流程化的利用程序,比如使用Struts构建特定的流程。但是这样没有办法将流程与实现分开,你会发现流程的定义分散在组成流程的各个元素中,没有特定的地方能够完全地描写全部流程。

Spring Web Flow是Spring MVC的扩大,它支持开发基于流程的利用程序,可以将流程的定义和实现流程行动的类和视图分离开来。

在介绍Spring Web Flow的时候,我们会暂且放下Spittr样例,而使用生产披萨定单的web程序。

使用的第1步是在项目中进行安装,那末就从安装开始吧。

在Spring中配置Spring Web Flow

Spring Web Flow是基于Spring MVC构建的,这就意味着所有的流程要求都需要经过Spring MVC的DispatcherServlet。我们需要在Spring利用上下文中配置1些Bean来处理流程要求并履行流程。

现在还没有支持使用Java来配置Spring Web Flow,所以没得选,只能在XML中进行配置。有1些Bean会使用Spring Web Flow的Spring配置文件命名空间来进行声明,因此我们需要在上下文定义XML文件中添加相应的命名空间:

<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⑵.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans⑶.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context⑶.0.xsd">

声明了命名空间后,就能够准备装配Web Flow的Bean了。

编写流程履行器

顾名思义,流程履行器(flow executor )就是用来驱动流程的履行。当用户进入到1个流程时,流程履行器会为该用户创建并启动1个流程履行器的实例。当流程暂停时(例如为用户展现视图时),流程履行器会在用户履行操作后恢复流程。

在Spring中,元素可以创建1个流程履行器:

虽然流程履行器负责创建和履行流程,但它其实不负责加载流程定义。这个要由流程注册表(flow registry)负责,下面会创建它。

配置流程注册表

流程注册表的工作就是加载流程定义,并让流程履行器可使用它们。可以在Spring中使用进行配置:

<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.xml结尾的XML文件都会被视为流程定义。

所有的流程都是通过其ID来进行援用的。使用元素,流程的ID就是相对base-path的路径,或是双星号所代表的路径,以下图展现了流程ID是如何计算的:
在使用流程定位模式时,流程定义文件相对于基本路径的路径将用作流程的id

另外,你也能够不使用base-path属性,直接显式地声明流程定义文件的位置:

<flow:flow-registry id="flowRegistry"> <flow:flow-location path="/WEB-INF/flows/springpizza.xml" /> flow:flow-registry>

这里使用了而不是,path属性直接指定了/WEB-INF/flows/springpizza.xml为流程定义文件。当这样定义时,流程的ID是从流程定义文件的文件名中获得的,这就是springpizza。

如果你希望更显示地指定流程ID,那末可以通过元素的id属性来进行设置。例如,要设定pizza作为流程ID,可以这样进行配置:

<flow:flow-registry id="flowRegistry"> <flow:flow-location id="pizza" path="/WEB-INF/flows/springpizza.xml" /> flow:flow-registry>

处理流程要求

正如前面的章节中提到的,DispatcherServlet会将要求分发给控制器,但是对流程而言,你需要FlowHandlerMapping来帮助DispatcherServlet将流程要求发送给Spring Web Flow。FlowHandlerMapping的配置以下:

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"> <property name="flowRegistry" ref="flowRegistry" /> bean>

FlowHandlerMapping装配了注册表的援用,这样它就知道如何将要求的URL匹配到流程上。例如,如果有1个ID为pizza的流程,FlowHandlerMapping就会知道如果要求的URL是/pizza的话,就会将其匹配到这个流程上。

但是,FlowHandlerMapping的工作仅仅是将流程要求定向到Spring Web Flow,响应要求的是FlowHandlerAdapter,它同等于Spring MVC的控制器,会对流程要求进行响应并处理。FlowHandlerAdapter可以像下面这样装配成1个Spring Bean:

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter"> <property name="flowExecutor" ref="flowExecutor" /> bean>

这个处理适配器就是DispatcherServlet和Spring Web Flow之间的桥梁。它会处理流程要求并管理基于这些要求的流程。在这里,它装配了流程履行器的援用,而后者是为要求履行流程的。

现在已配置了Spring Web Flow所需的Bean和组件,下面所需的就是真实的定义流程了。首先了解下流程的组成元素。

流程组件

在Spring Web Flow中,流程是由3个主要元素组成的:状态(state)、转移(transition)和流程数据(flow data)。状态是流程中事件产生的地点。如果将流程想象成公路旅行,那末状态就是路途上的城镇、路边饭店和风景点等。流程中的状态是业务逻辑履行、做出决策或将页面展现给用户的地方,而不是在公路旅行中买薯片或可乐这些行动。

如果说流程状态是公路上停下来的地点,那末转移就是连接这些点的公路。在流程上,需要通过转移从1个状态到达另外一个状态。

在城镇间旅行的时候,可能需要购买1些记念品、留下1下回想。类似的,在流程处理进程中,它要搜集1些数据:流程当前状态等。或许你很想将其称为流程的状态,但是我们定义的状态已有了另外的含义。

状态

Spring Web Flow定义了5种不同的状态,以下表所示。通过选择Spring Web Flow的状态几近可以把任意的安排功能构造成会话式的Web利用程序。虽然其实不是所有的流程都需要下表中的状态,但终究你可能会常常使用其中几个。

状态类型 作用
行动(Action) 流程逻辑产生的地方
决策(Decision) 决策状态将流程分为两个方向,它会基于流程数据的评估结果肯定流程方向
结束(End) 结束状态是流程的最后1站,进入End状态,流程就会终止
子流程(Subflow) 子流程状态会在当前正在运行的流程上下文中启动1个新的流程
视图(View) 视图状态会暂停流程并约请用户参与流程

首先了解下这些流程元素在Spring Web Flow定义中是如何表现的。

视图状态

视图状态用来为用户展现信息并使用户在流程中发挥作用。实际的视图实现可以是Spring支持的任意视图类型,但通常是用JSP来实现的。

在流程定义文件中,用来定义视图状态:

<view-state id="welcome" />

在这个简单的示例中,id属性有两个含义。其1,它定义了流程中的状态。其2,由于这里没有其他地方指定视图,那末它就指定了流程到达这个状态时要展现的逻辑视图名称为welcome。

如果要显示地指定另外1个视图名称,那末就能够使用view属性:

<view-state id="welcome" view="greeting" />

如果流程为用户展现了1个表单,你希望指定表单所绑定的对象,可使用model属性:

<view-state id="takePayment" model="flowScope.paymentDetails"/>

这里指定了takePayment视图将绑定流程范围内的paymentDetails对象。

行动状态

视图状态包括流程利用的用户,而行动状态则是利用程序本身在履行任务。行动状态1般会触发Spring所管理Bean的1些方法,并跟你讲方法调用的履行结果转移到另外一个状态。

在流程定义文件中,行动状态使用元夙来声明:

<action-state id="saveOrder"> <evaluate expression="pizzaFlowActions.saveOrder(order)" /> <transition to="thankYou" /> action-state>

虽然没有严格要求,但是元素1般都有1个子元素,该元素给出了行动状态要做的事情,expression属性指定了进入这个状态时要评估的表达式。本例中,给出的是SpEL表达式,这表明它将会找到ID为pizzaFlowActions的Bean,并调用其saveOrder()方法。

决策状态

流程有可能会依照线性履行下去,从1个状态到另外一个状态,没有其他的替换线路。但是更常见的是流程在某1个点根据流程当前情况进入不同的分支。

决策状态能够使得在流程履行时产生两个分支,它会评估1个Boolean表达式,根据结果是true还是false在两个状态转移当选择1个。在流程定义文件中,使用元夙来定义决策状态:

<decision-state id="checkDeliveryArea"> <if test="pizzaFlowActions.checkDeliveryArea(customer.zipCode)" then="addCustomer" else="deliveryWarning" /> decision-state>

其实不是单独工作的,元素是其核心,它是进行表达式评估的地方,如果表达式结果为true,流程会转向then属性指定的状态,为false会转向else指定的状态中。

子流程状态

或许你不会将利用程序的所有逻辑都写在1个方法里,而是将其分散到多个类、方法1起其他结构中。

一样的,将流程分成独立的部份也是个不错的主张。元素允许在1个正在履行的流程中调用另外一个流程:

<subflow-state id="order" subflow="pizza/order"> <input name="order" value="order"/> <transition on="orderCreated" to="payment" /> subflow-state>

这里,元素作为子流程的输入被用于传递定单对象。如果子流程结束的状态ID为orderCreated,那末本流程就会转移到ID为payment的状态。

结束状态

最后,所有的流程都要结束。这就是流程转移到结束状态时所做的。元素指定了流程的结束:

<end-state id="customerReady" />

当流程到达时,流程就会结束。接下来产生甚么要取决于以下几个因素:
- 如果结束的流程是个子流程,那末调用它的流程将会从处继续履行。的ID将会用作时间触发从开始的转移。
- 如果设置了view属性,那末就会渲染指定的视图。视图可以是相对流程的路径,也能够是流程模板,使用externalRedirect:前缀的会重定向到流程外部的页面,而使用flowRedirect:前缀的则会重定向到另外1个流程。
- 如果结束的流程不是子流程也没有配置view属性,那末这个流程就会结束。阅读器最后将会加载流程的基本URL地址,同时,由于没有活动的流程,所以会开始1个新的流程实例。

需要注意的是1个流程可能有多个结束状态。由于子流程的结束状态ID肯定了激活的事件,所以或许你会希望以多种结束状态来结束子流程,从而能够在调用流程中触发不同的事件,即便不是在子流程中,也有可能在结束流程后,根据流程的履行情况有多个显示页面供选择。

下面看1下流程是如何在状态间迁移的,如何在流程中通过定义转移来完成道路铺设。

转移

如前文所述,转移连接了流程中的状态。流程中除结束状态外的每一个状态,最少需要1个转移,这样就知道在状态完成时的走向。1个状态或许有多个转移,分别表示当前状态结束时可以履行的不同路径。

转移是通过元夙来定义的,作为其他状态元素(、和)的子元素。最简单的情势就是元素在流程中指定下1个状态:

<transition to="customerReady" />

属性to用于指定流程中的下1个状态。如果元素只使用了to属性,那末这个转移就会是当前状态的默许转移选项,如果没有其他可用转移的话,就会使用它。

更加常见的转移定义是基于事件的触发来进行的。在视图状态,事件通常会是用户采取的动作。在行动状态,事件是评估表达式得到的结果。而在子流程状态,事件取决于子流程结束状态的ID。在任意事件中,你可使用on属性来指定触发转移的事件:

<transition on="phoneEntered" to="lookupCustomer"/>

在示例中,如果触发了phoneEntered事件流程,就会进入lookupCustomer状态。

在抛出异常时,流程也可能进入另外一种状态。例如,如果没有找到顾客的记录,你可能希望流程转移到1个显示注册表单的视图状态,以下面:

<transition on-exception="com.springinaction.pizza.service.CustomerNotFoundException" to="registrationForm" />

属性on-exception和属性on10分类似,它是指定了要产生转移的异常而不是1个事件。

全局转移

在创建完流程后,或许你会发现有些状态使用了1些通用的转移。例如在全部流程中到处都有以下转移:

<transition on="cancel" to="endState" />

与其在多个流程状态中重复通用的转移,不如将其作为的子元素,从而作为全局转移

<global-transitions> <transition on="cancel" to="endState" /> global-transitions>

定义完全局转移,流程中所有的状态都会默许具有这个cancel转移。

流程数据

当流程从1个状态到达另外一个状态时,它会带走1些数据。有时这些数据很快就会被使用,比如直接展现给用户,有时这些数据需要在全部流程中传递并在流程结束时使用。

声明变量

流程数据是保存在变量中的,而变量可以在流程的任意位置进行援用,并且可以以多种方式进行创建。其中最简单的方式就是使用元素:

<var name="customer" class="com.springinaction.pizza.domain.Customer"/>

这里创建了1个新的Customer实例并将其放在customer变量中,这个变量可以在流程的任意状态下进行访问使用。

作为行动状态的1部份或说作为视图状态的入口,也能够使用元夙来创建变量:

<evaluate result="viewScope.toppingsList" expression="T(com.springinaction.pizza.domain.Topping).asList()" />

这里元素计算了1个SpEL表达式,并将结果放到toppingsList变量中,这个变量是视图作用域的。

类似的,元素也能够设置变量的值:

<set name="flowScope.pizza" value="new com.springinaction.pizza.domain.Pizza()" />

元素与元素类似,都是讲变量设置为表达式计算的结果。这里我们设置了1个流程范围的pizza变量,它的值为Pizza对象的新实例。

流程数据的作用域

流程中所携带的数据都有其各自的生命周期,这取决于保存数据的变量本身的作用域,以下表:

范围 生命周期
Conversation 最高层级的流程开始时创建,在最高层级的流程结束时烧毁。由最高层级的流程和其所有的子流程所同享
Flow 当流程开始时创建,在流程结束时烧毁。只在创建它的流程中是可见的
Request 当1个要求进入流程时创建,流程返回时烧毁
Flash 流程开始时创建,流程结束时烧毁。在视图状态解析后,才会被清除
View 进入视图状态时创建,退出这个状态时烧毁,只在视图状态内可见

当使用元素声明变量时,变量始终是流程作用域的,也就是在流程作用域内定义变量。当使用或时,作用域通过name或result属性的前缀指定。例如,将1个值赋给流程作用域的theAnswer变量:

<set name="flowScope.theAnswer" value="42"/>

到目前为止,我们已看到了Web流程的所有原材料,下面要将其进行整合了,完成1个完全的流程。

组合起来:披萨流程

首先从构建1个高层次的流程开始,它定义了订购披萨的整体流程,然后将其拆分为多个子流程。

定义基本流程

当顾客访问Spizza网站时,他们需要进行用户辨认、选择1个或多个披萨添加到定单、提供支付信息,然后提交定单,等待披萨上来,以下图:
网上购买披萨的流程

下面展现Spring Web Flow的XML流程定义来实现披萨定单的整体流程:

<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⑵.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>

流程定义中的第1件事就是声明order变量。每次流程开始的时候都会创建1个Order实例。Order类会包括关于定单的所有信息、顾客信息、订购的披萨和支付信息等。

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 Listpizzas; private Payment payment; public Order() { pizzas = new ArrayList(); customer = new Customer(); } //getters and setters }

流程定义的主要组成部份是流程的状态,默许情况下,流程定义文件中的第1个状态会是流程访问的第1个状态。本例中就是identifyCustomer状态(1个子流程)。也能够通过元素的start-state属性来指定任意状态为开始状态:

<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⑵.3.xsd" start-state="identifyCustomer"> ... flow>

辨认顾客、构建披萨定单和支付这样的活动比较复杂,其实不合适将其直接放在1个状态,而是以元素展现的。

流程变量order将在前3个状态中进行填充并在第4个状态中进行保存。identifyCustomer子流程使用了元夙来填充order的customer属性,将其设置为调用顾客子流程收到的输出。buildOrder和takePayment状态使用了不同的方式,它们使用将order流程变量作为输入,这些子流程就可以在其内部填充order对象。

在定单得到顾客、披萨和支付信息后,就能够对其进行保存。saveOrder是处理这个任务的行动状态。它使用来调用ID为pizzaFlowActions的Bean的saveOrder()方法,并将保存的定单对象传递进来。定单完成保存后会转移到thankCustomer。

thankCustomer状态是1个简单的视图状态,后台使用了/WEB-INF/flows/pizza/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⑻" /> <head><title>Spizzatitle>head> <body> <h2>Thank you for your order!h2> Finish ]]> body> html>

该页面提供了1个完成流程的链接,它展现了用户与流程交互的唯1办法。

Spring Web Flow为视图的用户提供了1个flowExecutionUrl变量,它包括了流程的URL。结束链接将1个_eventId参数关联到URL上,以便返回到Web流程时触发finished事件。这个事件将会使流程到达结束状态。

流程将会在结束状态完成。由于在流程结束后没有下1步做甚么具体信息,流程将会重新从identifyCustomer状态开始,以准备接受下1个定单。

下面还要定义identifyCustomer、buildOrder、takePayment这些子流程。

搜集顾客信息

对1个顾客,需要搜集其电话、住址等信息,以下面的流程图:
识别顾客流程

这个流程不再是线性的,而是有了分支。例如在查找顾客后,流程可能结束,也可能转到注册表单。一样的,在checkDeliveryArea状态,顾客可能会被告警,也多是不被告警。

程序清单:

<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⑵.0.xsd"> <input name="order" required="true" /> <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 id="cancel" /> <end-state id="customerReady" /> flow>

下面将这个流程定义分解成1个个的状态。

询问电话号码

welcome状态是1个很简单的视图状态,它欢迎访问Spizza网站的顾客并要求输入电话。它有两个转移:如果从视图触发phoneEntered事件,就会定向到lookupCustomer,另外1个就是在全局转移中定义用来响应cancel事件的cancel转移。

页面代码:

<%@ 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 Pizzatitle> 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输入。当进入视图状态时,流程暂停并等待用户采取1些行动。当用户提交表单时,流程履行键会在_flowExecutionKey输入域中返回,并在流程暂停的位置进行恢复。

还需要注意提交按钮的名称_eventId_部份是Spring Web Flow的1个线索,它表明了接下来要触发事件。当点击这个按钮提交表单时,就会触发phoneEntered事件,进而转移到lookupCustomer。

查找顾客

当欢迎顾客的表单提交后,顾客的电话号码将包括在要求参数中,并用于查询顾客。lookupCustomer状态的元素是查找产生的位置。它将电话号码从要求参数中抽取出来,并传递到pizzaFlowActions Bean的lookupCustomer()方法中。该方法要末返回Customer对象,要末抛出CustomerNotFoundException异常。

在前1种情况下,Customer对象会被设置到customer变量中(通过result属性)并默许的转移将流程带到customerReady状态。如果没有查到顾客,那末会抛出异常,流程会转移到registrationForm状态。

注册新顾客

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 Pizzatitle>head> <body> <h2>Customer Registrationh2> <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>

该表单绑定到了Order.customer对象上。

检查配送区域

顾客提供了地址后,需要确认住址是不是在配送范围内,因此使用了决策状态。

决策状态checkDeliveryArea有1个元素,它将顾客的邮编传递到pizzaFlowActions Bean的checkDeliveryArea()方法中,该方法会返回1个Boolean值。

如果顾客在配送范围内,那末流程将转移到addCustomer状态,否则进入deliveryWarning视图状态。deliveryWarnin视图:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head><title>Spring Pizzatitle>head> <body> <h2>Delivery Unavailableh2> <p>The address is outside of our delivery area. The order may still be taken for carry-out.p> <a href="${flowExecutionUrl}&_eventId=accept">Accepta> | <a href="${flowExecutionUrl}&_eventId=cancel">Cancela> body> html>

其中有两个链接,允许用户继续定单或取消定单。通过使用与welcome状态相同的flowExecutionUrl变量,这些链接分别触发流程中的accept和cancel事件。如果发送的是accept事件,那末流程会转移到addCustomer状态。否则,子流程会转移到cancel状态。

存储顾客数据

addCustomer有1个元素,它会调用pizzaFlowActions.addCustomer()方法,将order.customer流程参数传递进去。

1旦这个流程完成,就会履行默许转移,流程会转移到ID为customerReady的结束状态。

结束流程

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

注意,customerReady结束状态包括了1个元素。在流程中,它同等于Java的return语句。它会从子流程中传递1些数据到调用流程。例如,元素返回customer变量,这样披萨流程中的identifyCustomer子流程状态就能够将其指定给定单。

另外,如果用户在任意地方触发了cancel事件,将会通过cancel状态结束流程,这也会在披萨流程中触发cancel事件并致使转移到披萨流程的结束状态。

构建定单

下面就是肯定顾客想要甚么样的披萨,提示用户创建披萨并将其放入定单,如图:
通过订单子流程添加披萨

可以看到,showOrder状态位于定单子流程的中心位置。这是用户进入这个流程时的状态,也是用户添加披萨定单后转移的目标状态。它展现了定单确当前状态,并允许用户添加其他的披萨到定单中。

添加披萨定单时,会转移到createPizza状态。这是1个视图状态,允许用户对披萨进行选择。

在showOrder状态,用户可以提交定单,也能够取消。

<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⑵.0.xsd"> <input name="order" required="true" /> <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"
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生