19. Spring MVC 集成

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

19.1. 配置web.xml

使用 Spring MVC 的第一步是配置DispatcherServletweb.xml. 通常每个 Web 应用程序执行一次此作。spring-doc.cadn.net.cn

以下示例映射以/spring/DispatcherServlet. 一init-param用于提供contextConfigLocation. 这是 Web 应用程序的配置文件。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. 分派到流

DispatcherServlet将应用程序资源的请求映射到处理程序。 流是一种类型的处理程序。spring-doc.cadn.net.cn

19.2.1. 注册FlowHandlerAdapter

将请求分派到流的第一步是在 Spring MVC 中启用流处理。 为此,请安装FlowHandlerAdapter如下: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>

配置此映射可以让Dispatcher将应用程序资源路径映射到流注册表中的流。 例如,访问/hotels/bookingresource path 将导致对 ID 为hotels/booking. 如果找到具有该 ID 的流,则该流将处理请求。 如果未找到流,则查询调度程序有序链中的下一个处理程序映射,或者noHandlerFound返回响应。spring-doc.cadn.net.cn

19.2.3. 流处理工作流程

找到有效的流映射后,FlowHandlerAdapter根据 HTTP 请求提供的信息确定是开始该流的新执行还是恢复现有执行。 适配器采用许多与启动和恢复流执行相关的默认值:spring-doc.cadn.net.cn

  • HTTP 请求参数在所有启动流执行的输入映射中可用。spring-doc.cadn.net.cn

  • 当流执行结束而没有发送最终响应时,默认处理程序会尝试在同一请求中启动新的执行。spring-doc.cadn.net.cn

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

请参阅API 文档FlowHandlerAdapter了解更多信息。 您可以通过子类化或实现自己的FlowHandler,如下一节所述。spring-doc.cadn.net.cn

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

FlowHandler是可用于自定义流在 HTTP Servlet 环境中的使用方式的扩展点。 一个FlowHandlerFlowHandlerAdapter并负责: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);
}

要实现FlowHandler亚纲AbstractFlowHandler. 所有这些作都是可选的。如果不实现它们,则默认值适用。 只需覆盖所需的方法。 具体来说,您应该:spring-doc.cadn.net.cn

  • 覆盖getFlowId(HttpServletRequest)当流的 ID 无法直接从 HTTP 请求派生时。默认情况下,要调用的流的 ID 派生自pathInfo请求 URI 的一部分。例如http://localhost/app/hotels/booking?hotelId=1导致流 ID 为hotels/booking默认情况下。spring-doc.cadn.net.cn

  • 覆盖createExecutionInputMap(HttpServletRequest)当您需要对从HttpServletRequest.默认情况下,所有请求参数都被视为流输入参数。spring-doc.cadn.net.cn

  • 覆盖handleExecutionOutcome当您需要以自定义方式处理特定的流执行结果时。默认行为会将重定向发送到已结束流的 URL,以重新启动流的新执行。spring-doc.cadn.net.cn

  • 覆盖handleException当您需要对未处理的流异常进行细粒度控制时。当客户端尝试访问已结束或过期的流执行时,默认行为会尝试重新启动流。任何其他异常都会重新抛出给 Spring MVCExceptionResolver基础设施。spring-doc.cadn.net.cn

19.3.1. 示例FlowHandler

Spring MVC 和 Web Flow 之间的常见交互模式是将流重定向到@Controller当它结束时。FlowHandlerinstances 允许在不将流定义本身与特定控制器 URL 耦合的情况下完成此作。 以下示例FlowHandler重定向到 Spring 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";
		}
	}
}

由于此处理程序只需要以自定义方式处理流调用结果,因此不会覆盖任何其他内容。 这bookingConfirmed结果结果会导致重定向以显示新预订。 任何其他结果都会重定向回酒店的索引页面。spring-doc.cadn.net.cn

19.3.2. 部署自定义FlowHandler

安装自定义FlowHandler,您需要将其部署为 bean。 Bean 名称必须与处理程序应应用的流的 ID 匹配。 以下示例创建一个与hotels/booking流:spring-doc.cadn.net.cn

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

使用此配置,访问资源/hotels/booking启动hotels/booking使用自定义BookingFlowHandler. 预订流结束后,FlowHandler处理流执行结果并重定向到相应的控制器。spring-doc.cadn.net.cn

19.3.3.FlowHandler重 定向

一个FlowHandler处理FlowExecutionOutcomeFlowException返回一个String指示处理后要重定向到的资源。 在上一节所示的示例中,BookingFlowHandler重定向到booking/show资源 URIbookingConfirmed结果和hotels/index资源 URI 用于所有其他结果。spring-doc.cadn.net.cn

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

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

当您使用externalRedirect:指令与view-stateend-state——例如,view="externalRedirect:https://springframework.org".spring-doc.cadn.net.cn

19.4. 视图分辨率

除非另有说明,否则 Web Flow 会将选定的视图标识符映射到位于流工作目录中的文件。 对于现有的 Spring MVC 和 Web Flow 应用程序,外部ViewResolver可能已经为您处理了此映射。 因此,要继续使用该解析器并避免更改现有流视图的打包方式,您可以按如下方式配置 Web 流: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 Web Flow 中使用 Spring MVC 视图系统的工厂。 您可以使用它来配置现有的ViewResolver实例以及其他服务,例如自定义MessageCodesResolver. 您也可以让数据绑定使用 Spring MVC 的本机BeanWrapper通过将useSpringBindingflag 到true. 这是使用统一 EL 进行视图到模型数据绑定的替代方法。 有关更多信息,请参阅此类的 JavaDoc APIspring-doc.cadn.net.cn

19.5. 从视图发出事件信号

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

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

以下示例显示了同一窗体上的两个按钮,该按钮发出信号proceedcancel单击时的事件:spring-doc.cadn.net.cn

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

按下按钮时,Web Flow 会查找以_eventId_并将剩余的子字符串视为事件 ID。 因此,在此示例中,提交\_eventId_proceed成为proceed. 当有多个不同的事件可以从同一形式发出信号时,应考虑这种样式。spring-doc.cadn.net.cn

19.5.2. 使用隐藏的 HTML 表单参数来发出事件信号

以下示例显示了一种表单,该表单向proceed提交时的事件: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

以下示例显示了一个链接,该链接向cancel激活时的事件:spring-doc.cadn.net.cn

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

触发事件会导致将 HTTP 请求发送回服务器。 在服务器端,流处理从其当前事件中解码事件view-state. 此解码过程的工作原理特定于视图实现。 回想一下,Spring MVC 视图实现会查找名为_eventId. 如果没有\_eventId参数,则视图查找以\_eventId_并使用剩余的子字符串作为事件 ID。 如果这两种情况都不存在,则不会触发流事件。spring-doc.cadn.net.cn

19.6. 在页面上嵌入流

默认情况下,当流进入视图状态时,它会在呈现视图之前执行客户端重定向。 这种方法称为POST-REDIRECT-GET. 它的优点是将一个视图的表单处理与下一个视图的呈现分开。 因此,浏览器的“后退”和“刷新”按钮可以无缝工作,而不会引起任何浏览器警告。spring-doc.cadn.net.cn

通常,从用户的角度来看,客户端重定向是透明的。 但是,在某些情况下POST-REDIRECT-GET可能不会带来同样的好处。 例如,流可能嵌入在页面上,并由 Ajax 请求驱动,仅刷新属于该流的页面区域。 在这种情况下,不仅没有必要使用客户端重定向,而且在保持页面周围内容完整方面也不是所需的行为。spring-doc.cadn.net.cn

处理 Ajax 请求 部分介绍了如何在 Ajax 请求期间进行部分渲染。 本节的重点是解释如何在 Ajax 请求期间控制流执行重定向行为。 若要指示流应在“页面嵌入”模式下运行,请在启动流时附加一个额外的参数,如下所示:spring-doc.cadn.net.cn

/hotels/booking?mode=embedded

在“页面嵌入”模式下启动时,流不会在 Ajax 请求期间发出流执行重定向。 这mode=embedded参数仅在启动流时才需要传递。 您唯一需要关注的是使用 Ajax 请求,并且仅呈现更新显示流的页面部分所需的内容。spring-doc.cadn.net.cn

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

默认情况下,Web 流在进入每个视图状态时执行客户端重定向。 但是,如果您保持相同的视图状态(例如,没有to属性),则没有客户端重定向。 它适用于支持浏览器后退按钮的顶级流,同时仍利用 Ajax 和部分渲染来处理您保持在同一视图中的用例,例如表单验证、通过搜索结果分页等。 但是,过渡到新视图状态后始终会进行客户端重定向。 这使得在页面或模式对话框中嵌入流并执行多个视图状态而不导致整页刷新变得不可能。 因此,如果您的用例需要嵌入流,您可以在“嵌入”模式下启动它。spring-doc.cadn.net.cn

19.6.2. 嵌入式流示例

有关嵌入在页面和模式对话框中的流的示例,请参阅webflow-showcase项目。 您可以在本地签出源代码,像构建 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 闪存示波器

end-state执行内部重定向。 这在流结束时显示摘要屏幕时特别有用。 为了向后兼容,此功能默认处于禁用状态。 要启用它,请将saveOutputToFlashScopeOnRedirect在您的FlowHandlerAdaptertrue如下: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>

以下示例将confirmationNumber到 MVC 闪存范围,然后再重定向到summary屏幕。spring-doc.cadn.net.cn

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