您的位置:首页 > 编程语言 > Java开发

Spring Web Flow 简介

2016-03-14 13:30 357 查看

Spring Web Flow 简介

博客分类: 转载

SSH

最近在TSS上看到了一片介绍Spring Web Flow的文章,顺便就翻译了下来,SWF的正式版估计要到6月份才能看到了,目前的例子都是和Spring MVC集成的,但是换作和Struts集成也是非常方便的。

介绍
你是否觉得当你的Web应用越来越复杂,理解和管理页面流程—驱动你应用程序用例的乐谱—也越来越困难了呢?而被迫使用特定的方式做事情并且无法重用是不是让你感觉很累?你是否觉得使用了太多时间开发你自己特定的方法去解决普遍问题就像会话状态管理?

进入Spring Web Flow。

什么是Spring Web Flow?
Spring Web Flow (SWF) 是Spring Framework的一个脱离模块。这个模块是Spring Web应用开发模块栈的一部分,Spring Web包含Spring MVC。

Spring Web Flow 的目标是成为管理Web应用页面流程的最佳方案。当你的应用需要复杂的导航控制,例如向导,在一个比较大的事务过程中去指导用户经过一连串的步骤的时候,SWF将会是一个功能强大的控制器。

以下是一个受控制的导航的例子,使用UML状态图描述:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE webflow PUBLIC "-//SPRING//DTD WEBFLOW//EN"
"http://www.springframework.org/dtd/spring-webflow.dtd">

<webflow id="bookflight" start-state="obtainTripInfo">

<action-state id="obtainTripInfo">
<action bean="bookingActions" method="bindAndValidate"/>
<transition on="success" to="suggestItineraries"/>
<transition on="error" to="tryAgain"/>
</action-state>

<action-state id="suggestItineraries">
<action bean="bookingActions"/>
<transition on="success" to="displaySuggestedItineraries"/>
</action-state>

<view-state id="displaySuggestedItineraries" view="suggestedItenaries">
<transition on="startOver" to="cancel"/>
<transition on="select" to="selectItinerary"/>
</view-state>

<action-state id="selectItinerary">
<action bean="bookingActions"/>
<transition on="success" to="isPassengerInfoRequired"/>
</action-state>

<decision-state id="isPassengerInfoRequired">
<if test="${requestScope.passenger == null}" then="enterPassengerInformation"/>
<if test="${requestScope.passenger.preferences.alwaysConfirmPassengerInfo}"
then="enterPassengerInformation" else="displayReservationVerification"/>
</decision-state>

<subflow-state id="enterPassengerInformation" flow="passenger">
<attribute-mapper>
<input value="${requestScope.passenger.id}" as="passengerId"/>
</attribute-mapper>
<transition on="finish" to="displayReservationVerification"/>
</subflow-state>

<view-state id="displayReservationVerification" view="reservationVerification">
<transition on="startOver" to="cancel"/>
<transition on="assignSeats" to="chooseSeatAssignments"/>
<transition on="book" to="book"/>
</view-state>

<subflow-state id="chooseSeatAssignments" flow="seatAssignments">
<attribute-mapper>
<input value="${requestScope.passenger.id}" as="passengerId"/>
<input name="itinerary"/>
</attribute-mapper>
<transition on="finish" to="displayReservationVerification"/>
</subflow-state>

<action-state id="book">
<action bean="bookingActions"/>
<transition on="success" to="displayConfirmation"/>
</action-state>

<end-state id="displayConfirmation" view="reservationConfirmation"/>

<end-state id="tryAgain" view="tryAgain"/>

<end-state id="cancel" view="home"/>

</webflow>

图 2 - 基于XML的航空订票流程定义

就像你所看到的,仅仅是扫过XML定义,逻辑流程驱动的订票流程处理就已经可以清晰地辨认出来了,即使你都不了解Spring Web Flow实现细节。

如果你看得仔细点,你将会发现两个子流程产生了订票流程的子过程。第一个子流程指导用户输入乘客信息。第二个让用户分配他的座位。这个内嵌的流程扮演了“迷你应用程序模块”的角色,这是Spring Web Flow强大的功能之一。

你可以将这份定义上交给一位业务分析人员,并且她估计能看懂。更好的是你可以根据这个定义绘制一个可视化图表将其提交给业务分析人员。做这个的工具已经诞生了。

航空订票流程祥解
这篇文章的下一部分将逐块分解上面的航空订票流程定义,并且提供对话框演示Spring Web Flow是如何工作的。

流程定义

这是第一行的XML流程定义:

代码:
<webflow id="bookflight" start-state="obtainTripInfo">
</webflow>

webflow 元素定义了流程,指定它的id和start-date。id是一个简单的唯一的标识符,start-state是一个转变的初始状态,这发生在当一个新的流程会话在运行时被激活的时候。

所以,在业务案例上,当订票会话被激活的时候,它将转变为obtainTripInfo状态。

获得行程信息行为状态(Action State)

下面是obtainTripInfo状态定义。

代码:
<action-state id="obtainTripInfo">
<action bean="bookingActions" method="bindAndValidate"/>
<transition on="success" to="suggestItineraries"/>
<transition on="error" to="tryAgain"/>
</action-state>

记得当状态被进入,针对该状态的行为就发生了。正如你将看到的, 不同的状态类型有不同的执行动作。action state,正如obtainTripInfo,在进入的时候执行一个Action。该Action返回执行的逻辑结果,并且这个结果被映射到状态转变上。一切就是这么简单。

所以,在这个业务案例上,obtainTripInfo,当进入的时候执行bookingActions这个 Action的bindAndValidate方法。这个方法从浏览器绑定表单输入到一个Trip领域对象并且检验它。如果处理成功,就进入 suggestItineraries状态。如果错误发生,进入tryAgain状态。

订票Action

当在Spring IoC中使用Spring Web Flow的时候,action元素的bean属性涉及到Spring Application Context中定义的一个相同名称的Action实现。下面是bookingActions的定义:

web-context.xml

代码:
<bean id="bookingActions"
class="org.springframework.samples.bookflight.BookingActions">
<property name="bookingAgent" ref="myBookingAgent"/>
</bean>

这就允许我们的Action实现被Spring管理并且通过依赖注入进行配置。

建议路线行为状态

现在我们看一下下一个Action State,给定一个绑定的并且通过检验的Trip对象作为输入,返回一个建议的路线集合:

代码:
<action-state id="suggestItineraries">
<action bean="bookingActions"/>
<transition on="success" to="displaySuggestedItineraries"/>
</action-state>

下面是Action实现代码:

代码:
<view-state id="displaySuggestedItineraries" view="suggestedItenaries">
<transition on="startOver" to="cancel"/>
<transition on="select" to="selectItinerary"/>
</view-state>

就如你所看到的,displaySuggestedItineraries是一个view state - 一个我们还未讨论过的状态类型。一个视图状态,当进入的时候,导致执行流程暂停,并将控制返回给客户端同时根据配置的视图同时返回。随后,在用户思考过 后,客户端发出一个事件描述用户执行的Action。继续流程,事件的发生已经映射到了一个状态的转变,这个转变把用户带到了流程的下一步。

所以,在这个业务案例上,当进入displaySuggestedItineraries的时候suggestedIteneraries视图被渲染并且将控制返回给浏览器。然后用户选择路线之后点击“选择”按钮。这就出发了select事件,传递选择的路线id作为事件参数。

用户也可能选择startOver,这时候流程转变到了取消状态。

对于view属性,Spring MVC中,FlowControoler使用熟悉的 ModelAndView和ViewResolver构造,在Struts中,FlowAction用ActionForward。

客户端状态

在这个问题上你可能会问:

“...自从进入ViewState之后,执行流程暂停了,控制返回给了浏览器,那么流程如何重新拾起并且继续运行呢?”

答案就是客户端跟踪一个唯一的id用户标示流程执行点,并且将这个id放在input标签内,以便引起下一个事件。典型的做法是放在一个隐藏域内。

举个例子,在一个JSP文件里:

代码:
<input type="hidden" value="<c:out value="${flowExecution.id}"/>">

“是否需要乘客信息?” 决策状态(Decision State)

用户选择了她需要的路线之后,流程需要做一个上下文关系(contextual)的决策关于下一步执行什么。

需要特别指出的是,如果用户没有登录,或者她已经登录但是希望确认她的信息 - 例如她所使用的信用卡 - 流程控制需要允许她确定这些信息。另一方面,如果她已经登录并且希望直接进入预定页面,流程控制应该跳个这个可选步骤。

基本上需要做一个动态的决策重新考虑她的信息和偏好的。

决策状态最适合这个,看下面的定义:

代码:
<decision-state id="isPassengerInfoRequired">
<if test="${requestScope.passenger == null}" then="enterPassengerInformation"/>
<if test="${requestScope.passenger.preferences.alwaysConfirmPassengerInfo}"
then="enterPassengerInformation" else="displayReservationVerification"/>
</decision-state>

输入乘客信息子流程状态(SubFlow State)
处理乘客信息的过程逻辑上独立的。这是处理的一部分,但是在机票预定这个上下文环境之外它也可以独立存在。

子流程(Subflow)状态机制就是针对这个实现的。当进入一个子流程状态,这个子流程就被产生了。父流程挂起直到子流程结束。这让你可以把你的应用作为一系列自包含的模块看待,至于流程,你可以很容易的把多种情况统一处理。

看一下enterPassengerInformation子流程状态:

代码:
<subflow-state id="enterPassengerInformation" flow="passenger">
<attribute-mapper>
<input value="${requestScope.passenger.id}" as="passengerId"/>
</attribute-mapper>
<transition on="finish" to="displayReservationVerification"/>
</subflow-state>

flow 属性是这个进入这个流程的id,attribute-mapper 元素从子流程映射属性。输入映射将属性向下映射到子流程。输出映射将属性倒退回父流程当子流程结束的时候。你可以从这里看到表达式也是支持的。

所以,在这个业务用例上,当进入enterPassengerInformation状态,乘客流程就产生了。passengerId属性传递给这个流程作为输入。从这里,自流程作它需要做的。对于父流程来说这是一个黑箱。当子流程结束,父流程继续,应答最后结果并决定去哪执行下一步 — 在这里,去确认预定。

显示确认结束状态(End State)

最有一个状态类型在这里讨论:结束状态。当进入结束状态,活动的流程会话就结束了。在结束上面,所有与之相关的资源都被自动清理。

displayConfirmation结束状态在一条路线被被成功预定后显示确认信息:

代码:
<end-state id="displayConfirmation" view="reservationConfirmation"/>

当进入这个状态的时候,订票流程结束了并且显示reservationConfirmation视图。因为订票流程是根流程,并非子流程,所以任何分配的资源都会被自动清理。

注 意:结束流程如果是一个子流程,进入这个状态就会被认为是一个子流程结果并继续父流程。更特别的是,这个状态的ID在继续父流程的子流程的状态上被用作一个状态的转变。你可以从enterPassengerInformation子流程状态定义上看出来。注意它如何响应子流程的“完成”结果,是通过一个 “完成”结束状态。

流程部署
到这里,你了解了Spring Web Flow是关于什么的,并且你也看到了一个现实的例子。现在你要看到的就是如何部署这个流程定义到特定的环境中去执行,就行Spring MVC在一个Servlet环境下一样:

做这事是很容易的,这里你需要和Spring MVC一起使用:

代码:
<bean name="/booking.htm" class="org.springframework.web.flow.mvc.FlowController">
<property name="flow">
<ref bean="bookingFlow"/>
</property>
</bean>

<bean id="bookingFlow" class="org.springframework.web.flow.config.XmlFlowFactoryBean">
<property name="location" value="classpath:bookflight-flow.xml"/>
</bean>

这就自动将bookingFlow导出至/booking.htm这个URL在一个Servlet环境里。

高级主题
下面的部分介绍了一些SWF更高级的特性。

流程执行监听器(Flow Execution Listeners)
Flow Execution Listener 构造了一个观察者允许你监听并且对一个执行着的流程的生存周期作出反应。你可以使用这个特性作任何事,从一个状态的预处理到后期条件的检测,或则审计、安全处理。

流程执行存储策略(Flow Execution Storage Strategies)
一个执行着的流程的状态的存储机制是完全可插拔的。基于HttpSession的存储是默认的,但是SWF提供两种其他的存储方式:一个是使用服务器端连续的会话储存,另一种是使用完全的客户端序列化。定义你自己的存储方式,举个例子,譬如使用数据库存储,是不推荐的。

什么时候使用Spring Web Flow才适合你?
你应该注意到Spring Web Flow并不是一揽子全包的解决方案。正如你所看到的,这是一个有状态的系统能够自动管理这些由业务处理过程驱动的页面流程。它不能被当作简单的、无状态的解决方案。举个例子,它不能被用在一些需要自由导航的站点,一些可以让用户自由“点击周围任意链接”的站点。Spring Web Flow被设计为强大的受控制导航,可以指导用户按照一个清晰的业务目的和生存周期进行处理。

为了使得用例更具体,这里有一些“不错的流程”的例子,这些流程就适合使用SWF系统:

航空订票
税收管理
申请贷款

下面的一些例子是不适合使用SWF的:

索引页
欢迎页
菜单
简单表单流(一个页面)
Spring Web Flow打算要作为一个优秀的传统的控制器在任何Web环境下,就像Spring MVC、Struts、Tapestry、Web Work、JSP或者Portlets一样。一个单一的站点可以适当的组合使用简单的控制器管理Web流程。

路线图
Spring Web Flow 1.0 final 版本将随着Spring 1.3正式版发布,时间定在JavaOne大会前大概六月份的时候。就现在而言,只能期待正式、稳定的预览版。这个产品目前已经在特性集合和示例程序方面相当成熟了。

当开发小组给最终发布版砌上最后一块砖时,下面是一些最重要的特性我们长在着手完成:

整合
作为独立的库,Spring Web Flow很好与其他框架整合了。除了Spring MVC以外,已经提供了和Struts、Portlet MVC的整合,JSP和Tapestry的整合在最终版中也会见到。

流程管理
在Spring 1.2中,在MBeanServer中输出用于管理和监视的bean是很容易的。一个FlowExecutionMBean管理接口已经存在了,我们计划扩展以便可以从JMX控制台集中监控在服务器执行的所有流程的全局统计数据。

可插拔性
系统中的每个结构都可以做成可插拔的以获得简单的扩展或定制,甚至是从xml定义中。这包括状态和转变,以及其中的其他概念。

事务补偿
提供的特性和例子程序展示了在执行过程中使用事务补偿来回滚先前提交的事务,我们对这点很感兴趣。

总结
Spring Web Flow 是控制业务处理流程的有效解决方案。并且用起来也很有意思,如果你还没试过,那么你还等什么呢?

参考
Spring Web Flow is covered in the Core Spring training course offered by Interface21 -http://www.springframework.com/training

The Spring Framework, http://www.springframework.org/

The Spring Web Flow Wiki, http://opensource.atlassian.com/confluence/spring/display/WEBFLOW/Home

The kdonald blog, http://www.jroller.com/page/kdonald

Struts, http://struts.apache.org/

Java Server Faces, http://java.sun.com/j2ee/javaserverfaces/

Tapestry, http://jakarta.apache.org/tapestry

WebWork, http://www.opensymphony.com/webwork/

JMX, http://java.sun.com/jmx

JavaOne, http://java.sun.com/javaone/

译者
Nicholas@NirvanaStudio

2005-5-19

原文地址:http://www.theserverside.com/articles/content/SpringWebFlow/article.html

引用:
http://www.nirvanastudio.org/forum/topic.aspx?topicid=15&page=65535
_________________
Blog: http://nicholasdsj.blogdriver.com
MSN: http://spaces.msn.com/members/nicholasdsj
Nicholas@NirvanaStudio
http://www.nirvanastudio.org/jswiki/default.asp?title=Nicholas
http://www.nirvanastudio.org/jswiki
http://www.nirvanastudio.org/forum
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: