19. 春季MVC集成

本章展示了如何将Web Flow集成到Spring MVC的Web应用中。 这Booking-MVC示例应用是Spring MVC带Web Flow的好参考。 该应用是一个简化的旅游网站,允许用户搜索和预订酒店房间。spring-doc.cadn.net.cn

19.1. 配置web.xml

使用 Spring MVC 的第一步是配置调度器服务web.xml. 通常每个网页应用都会这样做一次。spring-doc.cadn.net.cn

以下示例映射了所有以 开头的请求/Spring/调度器服务. 一初始参数用于提供contextConfigLocation. 这是网页应用的配置文件。spring-doc.cadn.net.cn

<servlet>
	<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/web-application-config.xml</param-value>
	</init-param>
</servlet>

<servlet-mapping>
	<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
	<url-pattern>/spring/*</url-pattern>
</servlet-mapping>

19.2. 调度到流

调度器服务映射向处理器请求应用资源。 流是一种处理方式。spring-doc.cadn.net.cn

19.2.1. 注册流量处理适配器

向流调度请求的第一步是在 Spring MVC 中启用流处理功能。 为此,安装流量处理适配器如下:spring-doc.cadn.net.cn

<!-- Enables FlowHandler URL mapping -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
	<property name="flowExecutor" ref="flowExecutor" />
</bean>

19.2.2. 定义流映射

一旦启用了流量处理,下一步就是将特定的应用资源映射到你的流程中。 最简单的方法是定义一个FlowHandlerMapping如下:spring-doc.cadn.net.cn

<!-- Maps request paths to flows in the flowRegistry;
	e.g. a path of /hotels/booking looks for a flow with id "hotels/booking" -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
	<property name="flowRegistry" ref="flowRegistry"/>
	<property name="order" value="0"/>
</bean>

配置该映射可以调度将应用资源路径映射到流注册表中的流程。 例如,访问/酒店/预订资源路径会导致对流程的注册查询,ID 为酒店/预订. 如果找到带有该ID的流,该流将处理该请求。 如果找不到流,则查询调度员有序链中的下一个处理程序映射,或者未被发现回复已回应。spring-doc.cadn.net.cn

19.2.3. 流量处理工作流程

当找到有效的流映射时,流量处理适配器根据HTTP请求中呈现的信息,决定是重新开始该流的执行,还是恢复已有的执行。 该适配器在开始和恢复流执行时采用了多种默认设置:spring-doc.cadn.net.cn

  • HTTP请求参数会在所有起始流执行的输入映射中提供。spring-doc.cadn.net.cn

  • 当流执行结束时未发送最终响应,默认处理程序会尝试在同一请求中重新开始执行。spring-doc.cadn.net.cn

  • 未处理的异常会传播到调度,除非例外是NoSuchFlowExecutionException.默认处理器尝试从NoSuchFlowExecutionException通过重新开始执行。spring-doc.cadn.net.cn

参见API文档流量处理适配器更多信息请见。 你可以通过子类化或实现自己的来覆盖这些默认流处理器,如下一节所述。spring-doc.cadn.net.cn

19.3. 实现自定义流量处理程序

流处理器是可用于自定义 HTTP servlet 环境中流程使用方式的扩展点。 一个流处理器流量处理适配器并负责:spring-doc.cadn.net.cn

这些职责在定义中有所体现org.springframework.mvc.servlet.FlowHandler接口:spring-doc.cadn.net.cn

public interface FlowHandler {

	public String getFlowId();

	public MutableAttributeMap createExecutionInputMap(HttpServletRequest request);

	public String handleExecutionOutcome(FlowExecutionOutcome outcome,
		HttpServletRequest request, HttpServletResponse response);

	public String handleException(FlowException e,
		HttpServletRequest request, HttpServletResponse response);
}

实现流处理器亚纲抽象流处理器. 所有这些作都是可选的。如果你不实现这些,默认值就会生效。 你只需覆盖你需要的方法。 具体来说,你应该:spring-doc.cadn.net.cn

  • 覆盖getFlowId(HttpServletRequest)当你的流程ID无法直接从HTTP请求中推导出时。默认情况下,调用流程的ID由路径信息请求URI的一部分。例如http://localhost/app/hotels/booking?hotelId=1结果为酒店/预订默认。spring-doc.cadn.net.cn

  • 覆盖createExecutionInputMap(HttpServletRequest)当你需要对从中提取流量输入参数进行细致控制时,HttpServletRequest.默认情况下,所有请求参数都被视为流输入参数。spring-doc.cadn.net.cn

  • 覆盖handle执行结果当你需要以自定义方式处理特定的流程执行结果时。默认行为会向最终流程的URL重定向,以重新开始新的流程执行。spring-doc.cadn.net.cn

  • 覆盖handleException当你需要对未处理的流量异常进行细致控制时,默认行为是在客户端尝试访问已结束或过期的流执行时尝试重启流程。任何其他例外则重新投回春季MVCExceptionResolver基础设施默认。spring-doc.cadn.net.cn

19.3.1. 示例流处理器

Spring MVC 与 Web Flow 之间常见的交互模式是,流会重定向到@Controller当它结束的时候。流处理器实例允许在不将流定义本身与特定控制器 URL 耦合的情况下完成此作。 以下示例流处理器重定向到春季MVC控制器:spring-doc.cadn.net.cn

public class BookingFlowHandler extends AbstractFlowHandler {
	public String handleExecutionOutcome(FlowExecutionOutcome outcome,
										HttpServletRequest request, HttpServletResponse response) {
		if (outcome.getId().equals("bookingConfirmed")) {
			return "/booking/show?bookingId=" + outcome.getOutput().get("bookingId");
		} else {
			return "/hotels/index";
		}
	}
}

由于该处理程序只需以自定义方式处理流调用结果,其他内容不会被覆盖。 这预订已确认结果是重定向显示新预订。 任何其他结果都会重定向到酒店的索引页面。spring-doc.cadn.net.cn

19.3.2. 部署自定义流处理器

安装自定义流处理器你需要把它部署成豆子。 豆子名称必须与处理程序应应用的流ID相匹配。 以下示例生成一个与酒店/预订流:spring-doc.cadn.net.cn

<bean name="hotels/booking" class="org.springframework.webflow.samples.booking.BookingFlowHandler" />

通过这种配置,访问资源/酒店/预订发射酒店/预订通过使用自定义流程BookingFlowHandler. 当预订流程结束时,流处理器处理流执行结果并重定向到相应的控制器。spring-doc.cadn.net.cn

19.3.3.流处理器重 定向

一个流处理器该处理FlowExecution结果FlowException返回 a字符串用于指示处理后应重定向的资源。 在上一节示例中,BookingFlowHandler重定向至预订/演出资源 URI预订已确认结果及酒店/索引所有其他结果的资源URI。spring-doc.cadn.net.cn

默认情况下,返回的资源位置相对于当前的 servlet 映射。 这使得流量处理程序可以通过相对路径重定向到应用内的其他控制器。 此外,在需要更多控制的情况下,也支持显式重定向前缀。spring-doc.cadn.net.cn

支持的显式重定向前缀有:spring-doc.cadn.net.cn

当你使用外部重定向:指令与视图状态终态——例如,view=“externalRedirect:https://springframework.org”.spring-doc.cadn.net.cn

19.4. 查看分辨率

除非另有说明,Web流程会将选定的视图标识符映射到流工作目录内的文件。 对于现有的 Spring MVC 和 Web Flow 应用,外部ViewResolver很可能已经帮你做了这个映射。 因此,为了继续使用该解析器并避免更改现有流程视图的打包方式,你可以配置 Web Flow:spring-doc.cadn.net.cn

<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
	<webflow:location path="/WEB-INF/hotels/booking/booking.xml" />
</webflow:flow-registry>

<webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator"/>

<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
	<property name="viewResolvers" ref="myExistingViewResolverToUseForFlows"/>
</bean>

MvcViewFactoryCreator是允许你配置 Spring MVC 视图系统在 Spring Web Flow 中使用的工厂。 你可以用它来配置现有的ViewResolver实例以及其他服务,如自定义MessageCodesResolver. 你也可以让数据绑定使用Spring MVC的原生程序豆包装机通过设置useSpringBinding旗帜变为true. 这是使用统一EL进行视图到模型数据绑定的替代方案。 更多信息请参见该类的 JavaDoc APIspring-doc.cadn.net.cn

19.5. 从视图信号事件

当流进入视图状态它会暂停,将用户重定向到执行URL,并等待用户事件恢复。 事件通常通过激活按钮、链接或其他用户界面命令来进行信号。 事件在服务器端解码的方式取决于所使用的视图技术。 本节展示了如何从由JSP、Velocity或Freemarker等模板引擎生成的基于HTML的视图中触发事件。spring-doc.cadn.net.cn

19.5.1. 使用命名的HTML按钮来提示事件

以下示例展示了两个按钮,位于同一方向上,该信号进行取消事件分别点击:spring-doc.cadn.net.cn

<input type="submit" name="_eventId_proceed" value="Proceed" />
<input type="submit" name="_eventId_cancel" value="Cancel" />

当按下按钮时,Web Flow 会找到一个以 开头的请求参数名称_事件Id_并将剩余的子串视为事件ID。 在这个例子中,提交\_eventId_proceed成为进行. 当同一形式可以发出多种不同事件时,应考虑这种风格。spring-doc.cadn.net.cn

19.5.2. 使用隐藏的 HTML 表单参数来提示事件

以下示例展示了一个表示进行提交时的事件:spring-doc.cadn.net.cn

<input type="submit" value="Proceed" />
<input type="hidden" name="_eventId" value="proceed" />

这里,Web Flow检测到特殊情况\_eventId参数并以其值作为事件ID。 这种风格应仅在表格上能提示单一事件时考虑。spring-doc.cadn.net.cn

以下示例展示了一个信号取消激活时的事件:spring-doc.cadn.net.cn

<a href="${flowExecutionUrl}&_eventId=cancel">Cancel</a>

触发事件会将 HTTP 请求返回服务器。 在服务器端,流程负责从当前事件中解码视图状态. 解码过程的工作方式取决于视图的实现方式。 回想一下,Spring 的 MVC 视图实现会寻找一个名为_eventId. 如果没有\_eventId参数被找到,视图寻找以 开头的参数\_事件Id_并使用剩余的子串作为事件ID。 如果两者都不存在,则不会触发流事件。spring-doc.cadn.net.cn

19.6. 在页面上嵌入流程

默认情况下,当流程进入视图状态时,会在渲染视图前执行客户端重定向。 这种方法被称为重定向后获取. 它的优点是将一个视图的表单处理与下一个视图的渲染分离。 因此,浏览器的返回和刷新按钮可以无缝使用,不会触发任何浏览器警告。spring-doc.cadn.net.cn

通常,客户端重定向从用户视角是透明的。 然而,也有一些情况重定向后获取可能不会带来同样的好处。 例如,一个流程可能嵌入在页面上,由 Ajax 请求驱动,只刷新属于该流程的页面区域。 在这种情况下,不仅不需要使用客户端重定向,而且这也不是保持页面周围内容完整的理想行为。spring-doc.cadn.net.cn

处理 Ajax 请求部分解释了如何在 Ajax 请求中进行部分渲染。 本节重点解释如何在 Ajax 请求中控制流执行重定向行为。 为了表示流程应以“页面嵌入”模式运行,启动流程时添加一个额外参数,如下:spring-doc.cadn.net.cn

/hotels/booking?mode=embedded

当以“页面嵌入”模式启动时,流程不会在 Ajax 请求中发出流执行重定向。 这模式=嵌入参数只需在启动流时传递。 你唯一需要关注的是使用 Ajax 请求,并且只渲染更新显示流程页面部分所需的内容。spring-doc.cadn.net.cn

19.6.1. 嵌入式模式与默认重定向行为

默认情况下,Web Flow 在进入每个视图状态时会进行客户端重定向。 然而,如果你保持在同一视图状态(例如,没有attribute)在 Ajax 请求期间,客户端不会重定向。 它适合支持浏览器返回按钮的顶层流程,同时利用 Ajax 和部分渲染,适用于保持视图相同的场景,如表单验证、搜索结果分页等。 然而,切换到新视图状态后总是伴随着客户端重定向。 这使得在页面或模态对话框中嵌入流程并执行多个视图状态时,不会导致全页刷新,变得不可能。 因此,如果你的用例需要嵌入流程,你可以以“嵌入”模式启动它。spring-doc.cadn.net.cn

19.6.2. 嵌入式流示例

关于嵌入页面和模态对话框中的流程示例,请参见Webflow展示项目。 你可以在本地查看源代码,像构建 Maven 项目一样构建,然后导入到 Eclipse,具体如下:spring-doc.cadn.net.cn

cd some-directory
git clone https://github.com/spring-projects/spring-webflow-samples.git
cd spring-webflow-samples/webflow-showcase
mvn package
# import into Eclipse

19.7. 保存流向MVC闪光示波器的输出

终态执行内部重定向。 这在流程结束时显示摘要界面时尤其有用。 为了向后兼容,该功能默认被禁用。 要启用它,设置saveOutputToFlashScopeOnRedirect在你的流量处理适配器true如下:spring-doc.cadn.net.cn

<!-- Enables FlowHandler URL mapping -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
	<property name="flowExecutor" ref="flowExecutor" />
	<property name="saveOutputToFlashScopeOnRedirect" value="true" />
</bean>

以下示例补充确认编号在重定向到总结屏幕。spring-doc.cadn.net.cn

<end-state id="finish" view="externalRedirect:summary">
	<output name="confirmationNumber" value="booking.confirmationNumber" />
</end-state>