笔记37 Spring Web Flow——流程的组件
在Spring Web Flow中,流程是由三个主要元素定义的:状态、转移和 流程数据。
一、状态
Spring Web Flow定义了五种不同类型的状态。通过选择Spring Web Flow的状态几乎可以把任意的安排功能构造成会话式的Web应用。
状态类型 | 用途 |
行为(Action) | 行为状态是流程逻辑发生的地方 |
决策(Decision) | 决策状态将流程分成两个方向,它会基于流程数据的评估结果确定流程方向 |
结束(End) | 结束状态是流程的最后一战。一旦进入End状态,流程就会终止 |
子流程(Subflow) | 子流程状态会在当前正在运行的流程上下文中启动一个新的流程 |
视图(View) | 视图状态会暂停流程并邀请用户参与流程 |
1.1 视图状态
视图状态用于为用户展现信息并使用户在流程中发挥作用。实际的视 图实现可以是Spring支持的任意视图类型,但通常是用JSP来实现的。 在流程定义的XML文件中,<view-state>用于定义视图状态:
在这个简单的示例中,id属性有两个含义。它在流程内标示这个状 态。除此以外,因为在这里没有在其他地方指定视图,所以它也指定 了流程到达这个状态时要展现的逻辑视图名为welcome。 如果显式指定另外一个视图名,那可以使用view属性做到这 一点:
如果流程为用户展现了一个表单,如果要指明表单所绑定的对象,可以设置model属性:
1.2 行为状态
视图状态会涉及到流程应用程序的用户,而行为状态则是应用程序自 身在执行任务。行为状态一般会触发Spring所管理bean的一些方法并 根据方法调用的执行结果转移到另一个状态在流程定义XML中,行为状态使用<action-state>元素来声明。
尽管不是严格需要的,但是<action-state>元素一般都会有一个 <evaluate>作为子元素。<evaluate>元素给出了行为状态要做的 事情。expression属性指定了进入这个状态时要评估的表达式。在 本示例中,给出的expression是SpEL表达式,它表明将会找到ID 为pizzaFlowActions的bean并调用其saveOrder()方法。
1.3 决策状态
有可能流程会完全按照线性执行,从一个状态进入另一个状态,没有 其他的替代路线。但是更常见的情况是流程在某一个点根据流程的当 前情况进入不同的分支。决策状态能够在流程执行时产生两个分支。决策状态将评估一个 Boolean类型的表达式,然后在两个状态转移中选择一个,这要取决 于表达式会计算出true还是false。在XML流程定义中,决策状态 通过<decision-state>元素进行定义。典型的决策状态示例如下 所示:
1.4 子流程状态
将流程分成独立的部分。<subflow-state> 允许在一个正在执行的流程中调用另一个流程。这类似于在一个方法 中调用另一个方法。<input>元素用于传递订单对象作为子流程的输入。如果 子流程结束的<end-state>状态ID为orderCreated,那么流程将 会转移到名为payment的状态。
1.5 结束状态
最后,所有的流程都要结束。这就是当流程转移到结束状态时所做 的。<end-state>元素指定了流程的结束,它一般会是这样声明 的:
当到达<end-state>状态,流程会结束。接下来会发生什么取决于 几个因素:
-
- 如果结束的流程是一个子流程,那调用它的流程将会从 <subflow-state>处继续执行。<end-state>的ID将会用作 事件触发从<subflow-state>开始的转移。
- 如果<end-state>设置了view属性,指定的视图将会被渲染。 视图可以是相对于流程路径的视图模板,如果添 加“externalRedirect:”前缀的话,将会重定向到流程外部的 页面,如果添加“flowRedirect:”将重定向到另一个流程中。
- 如果结束的流程不是子流程,也没有指定view属性,那这个流 程只是会结束而已。浏览器最后将会加载流程的基本URL地址, 当前已没有活动的流程,所以会开始一个新的流程实例。
需要注意的是流程可能会有不止一个结束状态。子流程的结束状态ID确 定了激活的事件,所以你可能会希望通过多种结束状态来结束子流 程,从而能够在调用流程中触发不同的事件。即使不是在子流程中, 也有可能在结束流程后,根据流程的执行情况有多个显示页面供选 择。
二、转移
转移连接了流程中的状态。流程中除结束状 态之外的每个状态,至少都需要一个转移,这样就能够知道一旦这个 状态完成时流程要去向哪里。状态可以有多个转移,分别对应于当前 状态结束时可以执行的不同的路径。 转移使用<transition>元素来进行定义,它会作为各种状态元素 (<action-state>、<view-state>、<subflow-state>)的 子元素。最简单的形式就是<transition>元素在流程中指定下一 个状态:
属性to用于指定流程的下一个状态。如果<transition>只使用了 to属性,那这个转移就会是当前状态的默认转移选项,如果没有其 他可用转移的话,就会使用它。
更常见的转移定义是基于事件的触发来进行的。在视图状态,事件通 常会是用户采取的动作。在行为状态,事件是评估表达式得到的结 果。而在子流程状态,事件取决于子流程结束状态的ID。在任意的事 件中,你可以使用on属性来指定触发转移的 事件:
如果触发了phoneEntered事件,流程将会进 入lookupCustomer状态。 在抛出异常时,流程也可以进入另一个状态。例如,如果顾客的记录 没有找到,你可能希望流程转移到一个展现注册表单的视图状态。以 下的代码片段显示了这种类型的转移:
属性on-exception类似于on属性,只不过它指定了要发生转移的 异常而不是一个事件。在本示例 中,CustomerNotFoundException异常将导致流程转移 到registrationForm状态。
*全局转移
如果多个状态中都复用转移,则可以将<transition> 元素作为<global-transitions>的子元素,把它们定义为全局转 移。
在定义完这个全局转移后,流程中的所有状态都会默认拥有这个cancel转移。
三、流程数据
当流程从一个状态进 行到另一个状态时,它会带走一些数据。有时候,这些数据只需要很 短的时间(可能只要展现页面给用户)。有时候,这些数据会在整个 流程中传递并在流程结束的时候使用。
1.声明变量
流程数据保存在变量中,而变量可以在流程的各个地方进行引用。它能够以多种方式创建。在流程中创建变量的最简单形式是使用<var> 元素:
这里,创建了一个新的Customer实例并将其放在名为customer的 变量中。这个变量可以在流程的任意状态进行访问。作为行为状态的一部分或者作为视图状态的入口,你有可能会使 用<evaluate>元素来创建变量。例如:
在本例中,<evaluate>元素计算了一个表达式(SpEL表达式)并 将结果放到了名为toppingsList的变量中,这个变量是视图作用 域的。
类似地,<set>元素也可以设置变量的值:
<set>元素与<evaluate>元素很类似,都是将变量设置为表达式计 算的结果。这里,我们设置了一个流程作用域内的pizza变量,它的 值是Pizza对象的新实例。
2. 变量的作用域
流程中携带的数据会拥有不同的生命作用域和可见性,这取决于保存 数据的变量本身的作用域。Spring Web Flow定义了五种不同作用域, 如下图所示:
当使用<var>元素声明变量时,变量始终是流程作用域的,也就是在 定义变量的流程内有效。当使用<set>或<evaluate>的时候,作用 域通过name或result属性的前缀指定。例如,将一个值赋给流程作 用域的theAnswer变量: