Spring学习笔记4—流程(Spring Web Flow)
Spring Web Flow是Spring框架的子项目,作用是让程序按规定流程运行。
1 安装配置Spring Web Flow
虽然Spring Web Flow是Spring框架的子项目,但它并不是Spring框架的一部分,所以在构建基于流程的应用前,需要先添加Spring Web Flow所需的jar包。下载地址为:http://www.springframework.org/webflow
Spring Web Flow是构建与Spring MVC基础之上的。这意味着所有的流程请求都需要首先经过Spring MVC的DispatcherServlet。所以我们需要在Spring应用上下文中进行相应的配置。
1.1 FlowRegistry
FlowRegistry 是存放 flow 的仓库,每个定义 flow 的 XML 文档被解析后,都会被分配一个唯一的 id ,并以 FlowDefinition 对象的形式存放在 FlowResigtry 中。
<webflow:flow-registry id="flowRegistry">
<webflow:flow-location path="/WEB-INF/flows/shopping.xml" id=”shopping”/>
</webflow:flow-registry>
每个 flow 都必须要有 id 来标识,如果在配置中省略,那么该 flow 默认的 id 将是该定义文件(xml文件)的文件名去掉后缀所得的字符串(例如本例中如果去掉id="shopping",那么flow的id就是shopping.xml去掉后缀名.xml后的shopping作为id)。
1.2 FlowExecutor
FlowExecutor 是 Spring Web Flow 的一个核心接口,启动某个 flow ,都要通过这个接口来进行。从配置角度来说,只要保证有个 FlowExecutor 就可以了, Spring Web Flow 的默认行为已经足够。<webflow:flow-executor id="flowExecutor" />
1.3 Spring Web Flow 如何与 Spring Web MVC 整合在一起?
客户端发送的请求,先会由 servlet 容器(本教程示例中即为 Tomcat )接收, servlet 容器会找到相应的应用程序(本教程中即为 CartApp ),再根据 web.xml 的配置找到出符合映射条件的 servlet 来处理。 Spring Web MVC 中处理请求的 servlet 是 DispatcherServlet ,如果请求的路径满足 DispatcherServlet 的映射条件,则 DispatcherServlet 会找出 Spring IoC 容器中所有的 HandlerMapping ,根据这些 HandlerMapping 中匹配最好的 handler (一般情况下都是 controller ,即控制器)来处理请求。当 Controller 处理完毕,一般都会返回一个 view (视图)的名字,DispatcherServlet再根据这个view的名字找到相应的视图资源返回给客户端。
搞清楚 Spring Web MVC 处理请求的流程后,基本上就可以明白要整合 Spring Web MVC 与 Spring Web Flow 所需要的配置了。为了让客户端的请求变成执行某个 flow 的请求,要解决以下几个问题:
- 需要在某个 HandlerMapping 中配置负责处理 flow 请求的 handler (或 controller )
- 该handler (或 controller )要负责启动指定的 flow
- flow 执行过程中以及执行完成后所涉及的视图应呈现给客户端
1.4 FlowHandler 和 FlowController
现在,需要一种接收执行 flow 的请求,然后根据请求来启动相应 flow的handler (处理器), Spring Web Flow 2.0 提供了两种方案可供选择。第一种方案是自己编写实现了 FlowHandler 接口的类,让这个类来实现这个功能。第二种方案是使用一个现成的叫做 FlowController 的控制器。第一种方案灵活性比较大,在许多场合可能也是唯一的选择,但对每个 flow 都需要编写相应的 FlowHandler 。本教程的示例采用第二种方案,对 FlowHandler 的介绍可参看 Spring Web Flow 2.0 自带的文档。 FlowController 其实是个适配器,一般来讲,我们只要明白 FlowController 可根据客户端请求的结尾部分,找出相应的 flow 来执行。配置 FlowController只需指定FlowExecutor即可。
<bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">
<property name="flowExecutor" ref="flowExecutor"/>
</bean>
另外还需在 HandlerMapping 中指明 /shopping.do 请求由 flowController 来处理:
<bean id="viewMappings"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!-- /shopping.do 请求由 flowController 来处理 -->
<property name="mappings">
<value> /shopping.do=flowController </value>
</property>
<span style="white-space:pre"> </span>......
</bean>
1.5 FlowBuilder Services
flow-builder-services 属性的配置指明了在这个 flow-registry “仓库”里的 flow 的一些基本特性,例如,是用 Unified EL 还是 OGNL 、 model (模型)对象中的数据在显示之前是否需要先作转换,等等。在本示例中,我们需要在 flow-builder-services 属性中指明 Spring Web Flow 中所用到的 view ,由 Spring Web MVC 的“ View Resolver ”来查找,由 Spring Web MVC 的“ View Class”来解析,最后呈现给用户。
<!-- FlowRegistry 是存放 flow 的仓库,每个定义 flow 的 XML 文档被解析后,都会被分配一个唯一的 id ,并以 FlowDefinition 对象的形式存放在 FlowRes igtry 中 -->
<!-- 所有 flow的定义文件它的位置在这里进行配置, flow-builder-services 用于配置 flow 的特性 -->
<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
<webflow:flow-location path="/WEB-INF/flows/shopping.xml" id="shopping" />
<webflow:flow-location path="/WEB-INF/flows/addToCart.xml" id="addToCart" />
</webflow:flow-registry>
<!--Web Flow 中的视图通过 MVC 框架的视图技术来呈现 -->
<webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator" />
<!-- 指明 MVC 框架的 view resolver ,用于通过 view 名查找资源 -->
<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
<property name="viewResolvers" ref="viewResolver" />
</bean>
所有这些配置的目的无非是两个:一是要让客户端的请求转变成 flow 的执行,二是要让 flow 执行过程中、或执行结束后得到的视图能返还给客户端。
2 Spring Web Flow的组件
Spring Web Flow中,流程是由3个主要元素定义的:状态、转移和流程数据。
2.1 状态
Spring Web Flow定义了5种不同类型的状态(state)——行为、决策、结束、子流程和视图状态。
除了结束状态外,其他状态都可以转换到别的状态,一般通过在状态中定义转移(transition) 来实现到其他状态的转换,转换的发生一般由事件(event)来触发。
2.1.1 视图状态
视图状态用来为用户展现信息并是用户参与流程。实际的视图可以是任意类型,但通常是用JSP来实现。
在流程XML文件中,使用<view-state>定义视图状态。
<view-state id="viewCart" view="viewCart"> <transition on="submit" to="viewOrder"> </transition> </view-state>
2.1.2 行为状态
行为状态是程序自身在执行任务,一般情况下会触发Spring管理的Bean的一些方法并根据方法的返回结果转移到另一个状态。
在流程XML文件中,使用<action-state>定义行为状态。
<action-state id="addToCart"> <evaluate expression="cart.addItem(productService.getProduct(productId))" /> <transition to="productAdded" /> </action-state>
<action-state>中一般都会有一个<evaluate>元素,<evaluate>元素指定了行为状态要做的事情。
2.1.3 决策状态
决策状态能够在流程执行时产生两个分支。判断一个Boolean类型的表达式,然后在两个状态中选择一个。
在流程XML文件中,使用<decision-state>定义决策状态。
<decision-state id="ticketGrantingTicketExistsCheck"> <if test="flowScope.ticketGrantingTicketId neq null" then="hasServiceCheck" else="gatewayRequestCheck" /> </decision-state>
2.1.4 子流程状态
子流程状态用于在一个正在运行的流程中调用另一个流程。
在流程XML文件中,使用<subflow-state>定义子流程状态。
<subflow-state id="addProductToCart" subflow="addToCart"> <transition on="productAdded" to="viewCart" /> </subflow-state>
2.1.5 结束状态
结束状态用于结束一个流程。
在流程XML文件中,使用<end-state>定义结束状态。
<end-state id="returnToIndex" view="externalRedirect:servletRelative:/index.jsp"> </end-state>
如果结束的是一个子流程,那么流程将会从父流程的<subflow-state>处继续执行,子流程<end-state>的ID将触发父流程<subflow-state>的transition。
如果<end-state>设置了view属性,则指定的视图将会被渲染,加“externalRedirect:”前缀将重定向到流程外部的页面,而加“flowRedirect:”将重定向到另一个流程中。
如果结束的流程既不是子流程也没有指定view属性,那这个流程只是会结束,浏览器将会加载流程的基本URL地址,开始一个新的流程实例。
2.2 转移
流程中除了结束状态,其余的每个状态至少需要有一个转移,这样才能够知道一旦这个状态完成时流程要去向哪里。状态也可以有多个转移。
转移使用<transition>元素来定义,它会用作各种状态元素(<action-state>等)的子元素。
<transition on="addToCart" to="addProductToCart" />
属性to用于指定流程的下一个状态。
属性on用于指定触发转移的事件。(还有一个on-exception属性本例中没有给出,它用于指定触发转移的异常)
当多个状态使用了同一个转移时,可以设置一个全局转移来简化代码。
<global-transitions> <transition on="cancelShopping" to="returnToIndex"/> </global-transitions>
定义完这个全局转移后,流程中所有的状态都会默认拥有这个cancelShopping转移。
2.3 流程数据
流程状态在转移的过程中可能会需要“捎带”一些数据,这些数据就是流程数据,流程数据存放在变量中,变量能以多种形式创建,并且可以设置作用域。
2.3.1 声明变量
在流程中创建变量的最简单形式是使用<var>元素:
<var name="mycart" class="samples.webflow.Cart" />
上面代码创建了一个Cart实例并放到了名为mycart的变量中。
使用<evaluate>或者<set>元素也可以创建变量:
<evaluate result="conversationScope.cart" expression="mycart"/>
<set name="conversationScope.cart" value="mycart"/>
<evaluate>元素与<set>元素很类似,都是将变量设置为表达式计算的结果。这里变量cart的作用域设置的是conversationScope,接下来介绍一下流程数据的作用域。
2.3.2 定义流程数据的作用域
Spring Web Flow定义了5种作用域,如下表:
范围 | 作用域和可见性 |
Conversation | 在最高层的流程开始时创建,在最高层的流程结束时销毁。被最高层的流程和其所有的子流程共享 |
Flow | 当流程开始时创建,在流程结束时销毁。只有在创建它的流程中是可见的 |
Request | 当一个请求进入流程时创建,在流程返回时销毁 |
Flash | 当流程开始时创建,在流程结束时销毁。在视图状态渲染后,它也会被清除 |
View | 当进入视图状态时创建,当这个状态退出时销毁。只在视图状态内是可见的 |
当使用<var>元素声明变量时,变量始终是Flow作用域的,也就是在Flow作用域内定义变量。当使用<set>或<evaluate>时,作用域通过name或result属性的前缀指定。
参考:
http://www.cnblogs.com/xwdreamer/archive/2011/11/10/2296939.html
http://blog.csdn.net/hejingyuan6/article/details/46508821
转载请注明出处