Loading

Spring实战 八 Spring Web Flow

Spring Web Flow是将流程与实现解耦,单独定义流程(比如下订单->确认库存->付款->确认付款成功->创建订单->完成),而不是在Controller中实现逻辑的同时还要控制整个流程。

话说,Spring Web Flow好像不温不火的,而且它只支持XML配置,但是我真的觉得这玩意挺好玩的,马上要去拿核酸报告了,我先记一点点。

Spring Web Flow流程组件

Spring Web Flow将一套流程中的所有环节抽象成三个组件,分别是状态、转移和数据。

状态可以代表一切你看到的东西,包括一个前端视图,一个后端调用等等。而转移是用来连接状态的组件,比如我们现在有四个状态:付款,确认付款状态,创建订单和取消订单。转移帮助我们根据一些条件来在这些状态中跳转,比如从付款状态跳转到确认付款状态,再根据确认付款返回的结果进行向创建订单(付款成功)和取消订单(付款失败)这些状态的转移。数据就是在状态之间传递的数据。

状态

状态分为五种:

视图状态

视图状态就是向用户渲染一个视图,通常是jsp。它的定义很简单。

如下便定义了一个id为welcome的视图,并且这个id会作为逻辑视图名交给Spring来解析。

<view-state id="welcome"/>

当然也可以自己定义视图名

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

也可以使用表单绑定对象

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

这个flowScope.的语法一会会出现,先不用介意。

行为状态

行为状态用于指定一个任务,比如去数据库中查询库存

下面使用action-state定义了一个行为状态,其中使用SpEL表达式来进行了一次业务逻辑的调用,并且由于行为状态一般不是最终的状态,所以它使用了一次转移transition跳转到了下一个状态。

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

决策状态

很多时候我们的业务逻辑并不是一条线跑的,很多时候会因为错误或其他原因而需要做决策,最典型的例子就是如果付款成功创建订单,付款失败则取消订单。

下面的例子是检测用户是否在配送范围内,如果是则添加客户如果不是则发出warning通知客户。

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

子流程状态

子流程就是用于将一个大流程分成若干独立小流程。

下面是一个子流程定义,如果子流程的结束状态为orderCreated,那么跳转到payment状态。

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

结束状态

不管是子流程还是父流程,最终都要结束,所以我们需要一个结束状态来标注流程结束了。

它的声明也很简单,因为啥都不用做。

<end-state id="customerReady"/>
  1. 如果结束的流程是一个子流程,那么会从调用它的<subflow-state>处继续执行,<end-state>的id会作为<subflow-state>中的转移依据,做适当的转移。
  2. 如果结束状态设置了一个view属性,指定的视图会被渲染,如果添加externalRedirect:前缀则会重定向到流程外部的页面,如果添加flowRedirect:则将重定向到流程内部的页面。
  3. 如果都不是,流程就只是结束了。

转移

转移用来连接流程中的状态。

如下是一个最简单的转移,它没有任何条件,当转移列表中没有其他可应用的转移,它会作为默认转移。

<transition to="customerReady"/>

如下是当触发phoneEntered事件,流程进入lookupCustomer状态。事件通常是用户采取的动作,取决于子流程结束状态的id。

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

抛出异常时,也可以使用on-exception来转移状态

<transition on-exception="pizzaSystem.CustomerNotFoundException" to="registrationForm"/>

全局转移

有些转移需要在所有逻辑上都用到,就比如用户没登陆,那么让它先登录。或者用户选择了取消,那么结束流程。这时显然相比在每一处都写上这个转移,声明在全局范围更合适。

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

未完...去拿核酸结果喽!!!

回来了吃完饭了懒够了

流程数据

最后一个部分,就是流程数据的部分了。

使用var可以定义一个变量。如下定义了一个Customer类型的变量并命名为customer,这个变量可以在流程的任意状态进行访问。

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

除此之外,你还可能想要通过一个表达式来创建变量,viewScope将这个变量的作用域限制在了视图作用域。

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

类似的还有set命令,flowScope的作用范围是本次流程内。

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

共有五种不同的作用域

var是流程作用域,而evaluate和set可以设置作用域。

组合起来

嘶。。。这个看原书吧这部分,我感觉记下来意义不大。

posted @ 2021-09-14 14:55  yudoge  阅读(208)  评论(0编辑  收藏  举报