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"/>
- 如果结束的流程是一个子流程,那么会从调用它的
<subflow-state>
处继续执行,<end-state>
的id会作为<subflow-state>
中的转移依据,做适当的转移。 - 如果结束状态设置了一个
view
属性,指定的视图会被渲染,如果添加externalRedirect:
前缀则会重定向到流程外部的页面,如果添加flowRedirect:
则将重定向到流程内部的页面。 - 如果都不是,流程就只是结束了。
转移
转移用来连接流程中的状态。
如下是一个最简单的转移,它没有任何条件,当转移列表中没有其他可应用的转移,它会作为默认转移。
<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可以设置作用域。
组合起来
嘶。。。这个看原书吧这部分,我感觉记下来意义不大。