Spring Web Flow是一个Web框架,适用于元素按规定流程运行的程序。
8.1在Spring中配置Web Flow
Spring Web Flow 是构建于SpringMVC基础上的,所有流程请求都需要首先经过SpringMVC的DispatcherServlet。所以,首先先配置DispatcherServlet吧,这里采用web.xml的配置方式。
1、装配流程执行器(flow executor)
配置读取springConfig.xml,只能在XML中进行配置流程执行器。
当用户进入一个流程时,流程执行器会为用户创建并启动一个流程执行实例。flow:flow-executor元素会创建一个流程执行器。流程执行器负责创建和执行流程,但不负责加载流程定义,所以还还需要配置流程注册表。
2、配置流程注册表(flow registry)
流程注册表的工作是加载流程定义并让流程执行器能够使用它们。flow:flow-registry配置流程注册表。现配置在WEB-INF/flows文件夹下寻找流程定义。通过base-path属性指明所有以-flow.xml解为的XML文件都被视为流程定义。
所有的流程都是通过其ID来进行引用的(上面ID则为order)。也可以通过手动指定流程的位置,如下,则此时ID为order-flow。
3、处理流程请求
DispatcherServlet一般将请求分发给控制器,所以要使用流程时,需要一个FlowHandlerMapping来帮助DispatcherServlet将流程请求发送给Spring Web Flow。
FlowHandlerMapping装配了流程注册表的引用,例如,有一个order-flow.xml配置好的流程,ID为order,则知道如果请求的URL是/order的时候则需要匹配到这个流程来。
FlowHandlerMapping仅仅是将流程请求定向到Spring Web Flow上,相应请求的是处理器适配器(FlowHandlerAdapter),相当于SpringMVC的控制器,响应流程请求并对其进行处理。是Spring Web Flow和DispatcherServlet之间的桥梁。
8.2流程的组件
流程主要由状态、转移和流程数据组成。状态(State)是流程中事件发生的地点,是业务逻辑执行,做出决策或将页面展现给用户的地方。转移(transition)就是连接这些点的公路,通过转移从一个状态到另一个状态。流程数据指流程的当前状况。
1、状态
Spring Web Flow定义了五种不同类型的状态。通过选择Spring Web Flow的状态几乎可以把任意的安排功能狗造成会话式的Web应用。
•行为(Action)行为状态是逻辑发生的地方
•决策(Decision)决策状态将流程分为两个方向,基于流程数据的评估结果确定流程方向
•结束(End)结束状态是流程的最后一站,一旦进入则会终止
•子流程(Subflow)子流程状态会在当前正在运行的流程上下文中启动一个新的流程
•视图(View)视图状态会暂停流程并邀请用户参与流程
视图状态
视图状态用于为用户展现信息并使用户在流程中发挥作用。在流程定义的XML中,用于定义视图状态。
如上,id在流程中标识了这个状态,显式的指明了视图名为greeting,指明表单将绑定paymentDetails对象。
行为状态
行为状态是应用程序自身在执行任务。一般会出发Spring所管理bean的一些方法并根据方法调用的执行结果转移到另一个状态。使用声明行为状态。子元素给出了行为状态要做的事情,expression指定了进入这个状态时要评估的表达式。
如上,声明了一个saveOrder的行为状态,用evaluate声明要进入到saveOrder()方法,转移到thankYou状态。
决策状态
决策状态能够在流程执行时产生两个分支,流程在某个点根据流程的当前情况进入不同的分支。决策状态将评估一个Boolean类型表达式。通过元素定义决策状态。
如上,定义了一个决策状态,判断checkDeliveryArea()方法返回true或false,相应的转移到add或warn状态。
子流程状态
将流程分成独立的部分,称为子流程。允许在一个正在执行流程中调用另一个流程。
此子流程为pizza流程的子流程order
用于传递订单对象作为子流程的输入,子流程结束的的ID为orderCreated的话,则转移到payment的在站台。
结束状态
元素指定了流程的结束。
•如果结束的流程是一个子流程,则会从处继续执行,结束的ID会作为事件触发转移。如上图。
•如果设置了view属性,指定的视图将会被渲染。可以设置是转发或者重定向。
•如果流程结束的不是子流程也没有指定视图,浏览器会加载流程的基本URL地址。
2、转移
转移连接了流程中的状态。流程中除了结束状态之外的每个状态至少都需要一个转移。转移通过元素,作为状态的子元素进行定义。属性to指定流程的下一个状态,如果只使用了to属性,则此转移为当前状态的默认转移选项。更为常用可能会使用on属性来指定触发转移的事件。使用on-exception可以指定抛出异常时进入另一个状态。
全局转移
如果需要定义通用的转移,可以使用元素定义。
3、流程数据
声明变量
流程数据保存于变量中,可以在流程的各个地方进行引用。
最简单的形式是使用元素,此时创建了一个new的Customer并放到名为customer的变量中。
作为行为状态的一部分或者作为视图状态的入口,可能会使用元素创建变量
此时用一个SpEL表达式调用了asList()方法,将结果放到了toppingsList变量中,并且这个变量是视图作用域的。
也可以使用元素设置变量的值,此时将一个为流程作用域的变量piazz设置为new一个新对象。
定义流程数据的作用域
流程中携带的数据会拥有不同的生命作用域和可用性。
•Conversastion 最高级层级的流程开始时创建,最高级流程结束时销毁。
•Flow 在此流程开始时创建,此流程结束时销毁,只有真正创建的流程中可见。
•Request 当一个请求进入流程时创建,流程返回时销毁
•Flash 当流程开始时创建,结束时销毁,视图状态渲染后被清除。
•View 进入视图状态创建,退出此窗台销毁,只在视图状态内是可见的。
当使用时,变量始终是流程作用域的,在此流程内有效。使用和时可以由前缀指定。
8.3组合成披萨流程
1、定义基本流程
需要定义如上的订单流程,引导用户进行披萨订购。
定义主流程以及文件路径如上。用定义一个流程变量order,依次为顺序转移,主流程有三个子流程,使用subflow指定分别是pizza/order、pizza/customer、pizza/payment文件夹下的子流程。执行行为流程save(order),以及最后转移到感谢状态。订单domain对象如下,包含三个子对象Customer、Payment、Pizzas。
默认情况下,流程定义文件中的第一个状态是流程访问的第一个状态。也可以通过元素的 start-state属性手动指定开始状态:
流程中order将在前三个状态中进行填充并在第四个状态中保存。子流程可以使用元素来填充order的customer属性。也可以通过将order作为流程变量输入,在内部进行填充。随后在action状态中,调用保存方法,最后转移到视图状态。
最终的thankYou.jsp简单设置如下,通过一个url和controller交互,达到完成view-status的行为。
2、收集顾客信息(定义identifyCustomer子流程)
需要定义子流程identifyCustomer流程。
如上,定义用户子流程,分别涉及到三个jsp如下。
3、构建订单
定义好第一个子流程后,需要定义第二个子流程buildOrder。