8.2.1 状态

Spring Web Flow 定义了五种不同类型的状态,如表 8.1 所示。通过选择 Spring Web Flow 的状态几乎可以把任意的安排功能构造成会话式的 Web 应用。尽管并不是所有的流程都需要表 8.1 所描述的状态,但最终你可能会经常使用它们中的大多数。

状态类型

它是用来做什么的

行为(Action)

行为状态是流程逻辑发生的地方

决策 (Decision)

决策状态将流程分成两个方向,它会基于流程数据的评估结果确定流程方向

结束(End)

结束状态是流程的最后一站。一旦进入End状态,流程就会终止

子流程 (Subflow)

子流程状态会在当前正在运行的流程上下文中启动一个新的流程

视图(View)

视图状态会暂停流程并邀请用户参与流程

稍后我们将会看到如何将这些不同类型的状态组合起来形成一个完整的流程。但首先,让我们了解一下这些流程元素在 Spring Web Flow 定义中是如何表现的。

视图状态

视图状态用于为用户展现信息并使用户在流程中发挥作用。实际的视图实现可以是 Spring 支持的任意视图类型,但通常是用 JSP 来实现的。在流程定义的 XML 文件中,<view-state> 用于定义视图状态:

<view-state id="welcome" />

在这个简单的示例中,id 属性有两个含义。它在流程内标示这个状态。除此以外,因为在这里没有在其他地方指定视图,所以它也指定了流程到达这个状态时要展现的逻辑视图名为 welcome。

如果你愿意显式指定另外一个视图名,那可以使用 view 属性做到这一点:

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

如果流程为用户展现了一个表单,你可能希望指明表单所绑定的对象。为了做到这一点,可以设置 model 属性:

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

这里我们指定 takePayment 视图中的表单将绑定流程作用域内的 payment-Details 对象。(稍后,我们将会更详细地介绍流程作用域和数据。)

行为状态

视图状态会涉及到流程应用程序的用户,而行为状态则是应用程序自身在执行任务。行为状态一般会触发 Spring 所管理 bean 的一些方法并根据方法调用的执行结果转移到另一个状态。

在流程定义 XML 中,行为状态使用 <action-state> 元素来声明。这里是一个例子:

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

尽管不是严格需要的,但是 <action-state> 元素一般都会有一个 <evaluate> 作为子元素。<evalue> 元素给出了行为状态要做的事情。expression 属性指定了进入这个状态时要评估的表达式。在本示例中,给出的 expression 是 SpEL 表达式,它表明将会找到 ID 为 pizzaFlowActions 的 bean 并调用其 saveOrder() 方法。

Spring Web Flow 与表达式语言

在这几年以来,Spring Web Flow 在选择的表达式语言方面,经过了一些变化。在 1.0 版本的时候,Spring Web Flow 使用的是对象图导航语言(Object-Graph Navigation Language,OGNL)。随后的 2.0 版本又换成了统一表达式语言(Unified Expression Language,Unified EL)。在 2.1 版本中,Spring Web Flow 使用的是 SpEL。

尽管可以使用上述的任意表达式语言来配置 Spring Web Flow,但 SpEL 是默认和推荐使用的表达式语言。因此,当定义流程的时候,我们会选择使用 SpEL,忽略掉其他的可选方案。

决策状态

有可能流程会完全按照线性执行,从一个状态进入另一个状态,没有其他的替代路线。但是更常见的情况是流程在某一个点根据流程的当前情况进入不同的分支。

决策状态能够在流程执行时产生两个分支。决策状态将评估一个 Boolean 类型的表达式,然后在两个状态转移中选择一个,这要取决于表达式会计算出 true 还是 false。在 XML 流程定义中,决策状态通过 <decision-state> 元素进行定义。典型的决策状态示例如下所示:

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

你可以看到,<decision-state> 并不是独立完成工作的。<if> 元素是决策状态的核心。这是表达式进行评估的地方,如果表达式结果为 true,流程将转移到 then 属性指定的状态中,如果结果为 false,流程将会转移到 else 属性指定的状态中。

子流程状态

你可能不会将应用程序的所有逻辑写在一个方法中,而是将其分散到多个类、方法以及其他结构中。

同样,将流程分成独立的部分是个不错的主意。允许在一个正在执行的流程中调用另一个流程。这类似于在一个方法中调用另一个方法。

<suflow-state> 可以这样声明:

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

在这里,<input> 元素用于传递订单对象作为子流程的输入。如果子流程结束的 <end-state> 状态 ID 为 orderCreated,那么流程将会转移到名为 payment 的状态。

在这里,我有点超出进度了,我们还没有讨论到 <end-state> 元素和转移。我们很快就会在 8.2.2 小节介绍转移。对于结束状态,这正是接下来要介绍的。

结束状态

最后,所有的流程都要结束。这就是当流程转移到结束状态时所做的。<end-state> 元素指定了流程的结束,它一般会是这样声明的:

<end-state id="customerReady" />

当到达 <end-state> 状态,流程会结束。接下来会发生什么取决于几个因素:

  • 如果结束的流程是一个子流程,那调用它的流程将会从 <subflow-state> 处继续执行。<end-state> 的 ID 将会用作事件触发从 <subflow-state> 开始的转移。

  • 如果 <end-state> 设置了 view 属性,指定的视图将会被渲染。视图可以是相对于流程路径的视图模板,如果添加 externalRedirect: 前缀的话,将会重定向到流程外部的页面,如果添加 flowRedirect: 将重定向到另一个流程中。

  • 如果结束的流程不是子流程,也没有指定 view 属性,那这个流程只是会结束而已。浏览器最后将会加载流程的基本 URL 地址, 当前已没有活动的流程,所以会开始一个新的流程实例。

需要意识到流程可能会有不止一个结束状态。子流程的结束状态 ID 确定了激活的事件,所以你可能会希望通过多种结束状态来结束子流程,从而能够在调用流程中触发不同的事件。即使不是在子流程中,也有可能在结束流程后,根据流程的执行情况有多个显示页面供选择。

现在,已经看完了流程中的各个状态,我们应当看一下流程是如何在状态间迁移的。让我们看看如何在流程中通过定义转移来完成道路铺设的。

Last updated