基础
Web Flow将一个流程分为若干个状态(可以理解为步骤),每个流程由若干个状态组成,通过特性的方式在步骤之间进行跳转,协同完成整个流程。
Hello Jellyyyyyyyyyyyyyy !!
- 本文基于Spring Web Flow 2.4.5,其它版本配置方式可能略有不同,请参考相应版本的官方文档
- Maven依赖
maven库查询推荐地址:http://mvnrepository.com/
1 | <dependency> |
请求被DispatcherServlet拦截 -> 分发flow进行处理,返回view -> viewResolver解析 -> 返回请求
FlowRegistry用于注册流程实例,指定流程位置和流程id,并可自定义流程创建相关内容
1 | <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" /> |
1 | <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" id="bookHotel" /> |
定义流程属性
如下定义了一个带有属性caption,其值为”Books a hotel”的流程。属性的使用方法暂时不了解
1 | <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml"> |
使用通配定义流程位置
使用该方法并没有正确实验出id,这点作为参考
1 | <webflow:flow-location-pattern value="/WEB-INF/flows/**/*-flow.xml" /> |
使用基地址
使用基地址的flow的id为其path属性减去文件名,如下配置的id为/hotels/booking;如果path中没有路径信息,只有文件名,则id为文件名减去后缀。
1 | <webflow:flow-registry id="flowRegistry" base-path="/WEB-INF"> |
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
这里的id为booking/WEB-INF
, path=/flows/booking/booking.xml
时,id被确定为flows/booking
FlowRegistry是可以继承的,可以定义一个公用的注册器,在多个子注册器中继承该注册器
1 | <!-- my-system-config.xml --> |
使用FlowBuilder Services可以在build流程时自定义服务,比如视图解析器、EL表达式解析器、类型格式化和转换服务等。如无显式设定FlowBuilder Services,系统将使用默认实现。
1 | <webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices"> |
view-factory-creator用于视图解析工作,默认的creator可以支持Spring MVC支持的几种视图类型:JSP, Velocity,FreeMarker等。
1 | <webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices"> |
expression-parser用于定义表达式解析器,默认的解析器使用逻辑:当类路径下有Unified EL解析器时,则使用;没有时,则使用OGNL表达式解析器(官方文档在EL表达式一章和系统设置一章关于默认表达式解析说法有出入,自己认为比较可信的是Spring Web Flow 2.1以后,默认使用Spring EL表达式解析器)。
下面是手动配置成Unified EL解析器的方式:
1 | <webflow:flow-builder-services expression-parser="expressionParser"/> |
详细内容参见本系列视图渲染相关章节或官方文档
1 | <webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" ... /> |
1 | public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean { |
FlowExecutor用于执行flow并管理flow执行过程,常见的自定义项有:设置监听器,监听流程执行过程并作出相应,如security监听器,用于监听并控制流程的访问权限;调整flow的部分持久化选项等。
1 | <!-- 这里是为特定的flow应用该监听器,当不设criteria属性时,将对所有flow应用 --> |
1 | <webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry"> |
1 | <servlet> |
该步骤是使得Web Flow能够适配Spring MVC
当一个对flow的请求被接收到时,FlowHandlerAdapter会决定是启动一个全新的flow还是继续之前的流程(如从一个view-state跳转到下一个state就属于继续之前的流程),继续之前的flow是需要在请求有相关信息才行。有关这一方面,web flow默认有如下设定:
1.Http请求参数在任何情况下都是可以使用的
2.当一个flow执行结束,且结束时没有想浏览器发送最后的响应时,默认的handler会尝试在同一个request中启动一个新的flow执行
3.除了NoSuchFlowExecutionException异常外,所有其它异常都会以冒泡的方式抛到Dispatcher中。这是因为默认的handler会尝试自动从该异常中恢复过来,恢复的方式是新开一个全新的flow执行
针对大多数设定,都可以通过实现FlowHandlerAdapter类的子类进行自定义。
1 | <!-- Enables FlowHandler URL mapping --> |
1 | <!-- Maps request paths to flows in the flowRegistry; |
有了Flow的基本配置和这里的两个Spring MVC的集成配置,请求就能够映射到flow中了。接下来就是写Flow了。想要快速上手的可忽略本文后面的内容,转而直接看本系列其它文章。
- 返回flow的id以用于执行该flow
- 在新流程开始时创建输入
- 在流程结束时处理流程的输出
- 在流程发生异常时,处理这些异常
其主要方法如下:
1 | public interface FlowHandler { |
该接口的直接实现为AbstractFlowHandler,当我们想要自定义某个设定时,继承该抽象类并重写其中的方法即可。
1 | <!-- 这里的BookingHandler只是一个例子,实际用时替换成我们自己的Handler --> |
1 | public class BookingFlowHandler extends AbstractFlowHandler { |
默认情况下,该方法返回的地址是相关于当前Servlet的。但是我们也可以显式地指定一些前缀来扩展重定向的范围
– servletRelative
: - 相对于当前Servlet重定向
– contextRelative
: - 相对于当前应用重定向
– serverRelative
: - 相对于当前服务器的基地址重定向
– http:// or https://
- 重定向到一个完整的URI地址
相同的前缀同样适用于声明state时的view属性配上externalRedirect
的情况
1 | <end-state view="externalRedirect:http://springframework.org"/> |
—————————————————–手动分割线——————————————————————————————
1 | public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { |
1 | @Configuration |
1 | @Configuration |
最开始对flow id的认识不够,导致不知道到底该如何定位到flow,其实访问方式就是 …/appName/flowId
Web MVC Environment null not supported
启动时正常,访问flow时报异常java.lang.IllegalStateException: Web MVC Environment null not supported
是在创建viewFactory时出错,由于我没有采用在config中声明bean的方式创建MvcViewFactoryCreator,因此出现找不到mvc环境的问题,原来是spring在自动检测并创建bean时,会同时设置该bean的环境,因此不能自己随意采用new的方式创建这些配置类JSP文件不解析
出现JSP文件不经过解析就直接传送给了浏览器的问题(对如下阐述的原理并不是很清楚)
url-pattern为”/*”时,能够匹配到任何路径,因此当controller返回.jsp文件时,也会被拦截,从而返回jsp源码(这里不是很理解,主要是跟自己认识的spring mvc处理流程有差别)。
url-pattern为”/“时,只能够匹配不带后缀的路径,因此jsp就不会被dispatcherServlet拦截,而是会被jspServlet拦截并处理。但是”/“在配置使能的情况下也能够拦截并允许静态资源的访问
虽然之前没有听说过Spring Web Flow这个东西,听说之后上网查了一下又有很多人不屑于使用web flow,但自己目前工作上的项目要用到它,因此学习是很有必要的。当学习一项新技术时,最快捷的方式就是上网搜索速成教程,看了一些,总觉得有些不得要领,于是转向官方文档。又怕自己久了会忘,故此写一套学习笔记,详细地介绍使用方法。该笔记参考官方文档,自己加以整理而成。
什么是web flow,这里也不免俗地引用一下百度百科的内容(也基本上是官方介绍的翻译。。。):
Spring Web Flow (SWF) 是Spring Framework的一个脱离模块。这个模块是Spring Web应用开发模块栈的一部分,Spring Web包含Spring MVC。Spring Web Flow 的目标是成为管理Web应用页面流程的最佳方案。当你的应用需要复杂的导航控制,例如向导,在一个比较大的事务过程中去指导用户经过一连串的步骤的时候,SWF将会是一个功能强大的控制器。