项目中用到了webflow的相关知识,就上网学习了一下,借鉴他人的经验,做了一个简单的example。
1、首先解释一下 为什么使用webflow?
web应用程序的三种范围:request,session,appcation。
1)request适合存放大量的临时数据。
2)session适合存放本次会话需要保留的数据。
3)application适合存放那些与应用程序全局相关的数据。
session中存放大量的数据会严重影响效率,request的存放有效范围有限,那么就需要一种,介于session和request之间的范围,spring提过了flow解决这个问题。(还有一种conversion, 与flow基本相同,唯一不同在于 conversation 范围内的对象所在的 flow 如果调用了其他 subflow ,那么在 subflow 中也可访问该对象)。
flow使用于比较复杂的,有状态的,需要在多个页面之间跳转的业务流程问题。
2、案例:模拟简单的购物车流程
购物车-〉显示订单-〉确认订单
1)Spring web flow关注的是业务的流程。是Spring Web MVC的一个扩展,所以需要对它进行配置。
2)配置web.xml文件
<servlet>
<servlet-name>CartServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/web-application-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CartServlet</servlet-name>
<url-pattern>/jsp/*</url-pattern>
</servlet-mapping>
配置 web-application-config.xml
<!-- 搜索 samples.webflow 包里的 @Component 注解,并将其部署到容器中 -->
<context:component-scan base-package="samples.webflow" />
<!-- 启用基于注解的配置 -->
<context:annotation-config />
<import resource="webmvc-config.xml"/>
配置webmvc-config.xml
webmvc-config.xml主要配置mvc,添加一个viewResolver(视图解析器),用于将视图名解析成真实的视图资源。再配置URL请求的handler(处理器),用于将url请求定向到某个控制器。viewMappings 中的/shopping.do=flowController指明不管设成 /shopping.do 还是设成 /shopping ,或者 /shopping.htm ,效果都是一样的, flowController 都会去找 id 为 shopping的flow来执行。
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="viewMappings"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!-- /shopping.do 请求由 flowController 来处理 -->
<property name="defaultHandler">
<!-- UrlFilenameViewController 会将 "/index" 这样的请求映射成名为 "index" 的视图 -->
<bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
</property>
<property name="mappings">
<value> /shopping.do=flowController </value>
</property>
</bean>
<bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">
<property name="flowExecutor" ref="flowExecutor"/>
</bean>
配置webflow-config.xml和shopping.xml(业务流程的具体流动过程)
webflow-config.xml中配置仓库们的总的配置
<webflow:flow-executor id="flowExecutor"/>
<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>
flowExecutor是web flow的一个核心接口,启动某个flow,要通过这个接口进行,会被flowController作为参数。
flowRegistry是存放flow的仓库,每个定义flow的xml文件都会被分配一个唯一的id, 以FlowDefinition对象的形式存放在FlowRegistry中。每个flow都要有id标识,如果省略,那么flow默认的id就是文件名称,删除后缀的字符串,例如shopping.xml的默认id是shopping。
flow-builder-services是flow-registry的一个重要属性,它指明了flow-registry仓库中flow的一些基本特性,在本案例中,它用来指明flow中所用到的view,由viewResolver来查找,有view class来解析,最后呈现给用户。
shopping.xml中是具体的业务流程
<view-state id="viewCart" view="viewCart">
<transition on="submit" to="viewOrder"></transition>
</view-state>
<view-state id="viewOrder" view="viewOrder">
<transition on="submit" to="viewConfirmed"></transition>
</view-state>
<view-state id="viewConfirmed" view="viewConfirmed">
<transition on="returnToIndex" to="returnToIndex"></transition>
</view-state>
<end-state id="returnToIndex" view="externalRedirect:servletRelative:/index.jsp"></end-state>
web flow中的元素:五种state,分别是Action State、View State、Subflow State、Decision State、End State,transition来实现到其他state的转换,转换的发生一般由事件来触发。
view是视图资源的名字, 是viewResolver查找的名字。最后的end-state中externalRedirect用在view名字中,表示所指向的资源是在flow的外部,servletRelative则表明所指向资源的路径起始部分与flow所在servlet相同。
3)添加jsp文件
viewCart.jsp
<body>
<h1>View Cart</h1>
<a href="${flowExecutionUrl}&_eventId=submit">Submit</a>
</body>
viewOrder.jsp
<h1>Order</h1>
<a href="${flowExecutionUrl}&_eventId=confirm">Confirm</a>
orderConfirmed.jsp
<h1>Order Confirmed</h1>
<a href="${flowExecutionUrl}&_eventId=returnToIndex">Return to index</a>
flowExecutionUrl表示flow执行到当前状态时的url,它的值由spring web flow 框架的代码赋值,并放入相应的model中供view访问。它的值包含flow在执行过程中会为每一个状态生成唯一的key,因此不可以用其他手段获得。_eventId与shopping.xml中的transition的on属性的值对应。在接收到_eventId的值是,相应的transition就会执行。
3、Spring Web Flow 如何与 Spring Web MVC 整合在一起?
接收servlet 容器会找到相应的应用程序(本教程中即为 CartApp ),客户端发送的请求,先会由 servlet 容器,再根据 web.xml 的配置找到出符合映射条件的 servlet 来处理。
Spring Web MVC 中处理请求的 servlet 是 DispatcherServlet ,如果请求的路径满足 DispatcherServlet 的映射条件,则 DispatcherServlet 会找出 Spring IoC 容器中所有的 HandlerMapping ,根据这些 HandlerMapping 中匹配最好的 handler (一般情况下都是 controller ,即控制器)来处理请求。
当 Controller 处理完毕,一般都会返回一个 view (视图)的名字,DispatcherServlet再根据这个view的名字找到相应的视图资源返回给客户端。1)这里的flow是flowController,在webmvc-config.xml中配置的。用于接收指定flow的请求,然后根据请求启动相应的flow。FlowController是webflow中自带的现成的控制器,可以根据客户端请求的结尾部分,找出相应的flow来执行,需要指定flowExecutor。
另外还有一个FlowHandler可以实现该功能,内部需要研发人员实现,灵活性比较大,可以参见Spring Web Flow文档。
4、业务逻辑代码在调用后得到的数据如何保存、传递?
Spring Web Flow 的定义中可直接使用表达式语言( Expression Language ),前面的代码都是用的 Unified EL ,对于习惯用 OGNL 的开发人员,可通过 flow-builder-services 的配置改成使用 OGNL 。不管是哪一种表达式语言, Spring Web Flow 都提供了一些固定名称的变量,用于数据的保存、传递。在 Spring Web Flow 的解决方案 一节中,已经提到 Spring Web Flow 所着力解决的问题即是数据存取范围的问题,为此, Spring Web Flow 提供了两种比较重要的范围,一是 flow 范围,另一个是 conversation 范围。通过 flowScope 和 conversationScope 这两个变量, Spring Web Flow 提供了在这两种范围里存取数据的方法。清单 24演示了如何将业务逻辑代码执行的结果存放到flow范围中。
<evaluate expression="productService.getProducts()" result="flowScope.products" />
注意:Spring Web Flow 2.0 在默认配置下,flowScope 和 conversationScope 的实现依赖于 Java 序列化和反序列化技术,因此存放于 flowScope 或 conversationScope 中的对象需要实现 java.io.Serializable 接口。
Spring Web Flow 还提供了大量其他的变量,以方便数据的存取。如 viewScope 范围即是从进入 view-state 至退出 view-state 结束, requestScope 即和一般的 request 范围没什么区别,等等。另外还有一些用于获取 flow 以外数据的变量,如 requestParameters 、 messageContext 等等。具体变量的列表可参看 Spring Web Flow自带的文档。