19. Spring MVC 集成

本章介绍如何将 Web Flow 集成到 Spring MVC Web 应用程序中。 示例应用程序是带有 Web Flow 的 Spring MVC 的良好参考。 此应用程序是一个简化的旅游网站,允许用户搜索和预订酒店房间。booking-mvcspring-doc.cn

19.1. 配置web.xml

使用 Spring MVC 的第一步是在 in 中配置 . 通常,每个 Web 应用程序执行一次此操作。DispatcherServletweb.xmlspring-doc.cn

以下示例将所有以 开头的请求映射到 。 An 用于提供 . 这是 Web 应用程序的配置文件。/spring/DispatcherServletinit-paramcontextConfigLocationspring-doc.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. 分派到 Flows

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

19.2.1. 注册FlowHandlerAdapter

将请求分派给流的第一步是在 Spring MVC 中启用流处理。 为此,请安装 ,如下所示:FlowHandlerAdapterspring-doc.cn

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

19.2.2. 定义流映射

启用流处理后,下一步是将特定应用程序资源映射到您的流。 最简单的方法是定义 ,如下所示:FlowHandlerMappingspring-doc.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 的流,则该流将处理请求。 如果未找到流,则查询调度程序的有序链中的下一个处理程序映射或返回响应。Dispatcher/hotels/bookinghotels/bookingnoHandlerFoundspring-doc.cn

19.2.3. 流处理工作流

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

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

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

  • 未经处理的异常将传播到 ,除非异常是 .默认处理程序尝试通过重新开始执行来从 a 中恢复。DispatcherNoSuchFlowExecutionExceptionNoSuchFlowExecutionExceptionspring-doc.cn

有关更多信息,请参阅 FlowHandlerAdapter 的 API 文档。 您可以通过子类化或实现自己的 default 来覆盖这些默认值,如下一节所述。FlowHandlerspring-doc.cn

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

FlowHandler是可用于自定义流在 HTTP Servlet 环境中的使用方式的扩展点。 A 由 使用,并负责:FlowHandlerFlowHandlerAdapterspring-doc.cn

接口的定义说明了这些职责:org.springframework.mvc.servlet.FlowHandlerspring-doc.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);
}

要实现 , 子类 . 所有这些操作都是可选的。如果不实施它们,则应用默认值。 您只需覆盖所需的方法。 具体来说,您应该:FlowHandlerAbstractFlowHandlerspring-doc.cn

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

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

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

  • 当您需要对未处理的流异常进行精细控制时,Override (覆盖)。默认行为会在客户端尝试访问已结束或过期的流执行时重新启动流。默认情况下,任何其他异常都会重新抛出到 Spring MVC 基础结构中。handleExceptionExceptionResolverspring-doc.cn

19.3.1. 示例FlowHandler

Spring MVC 和 Web Flow 之间的一种常见交互模式是,当流结束时重定向到 。 实例允许这样做,而无需将流定义本身与特定控制器 URL 耦合。 以下示例重定向到 Spring MVC 控制器:@ControllerFlowHandlerFlowHandlerspring-doc.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";
		}
	}
}

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

19.3.2. 部署自定义FlowHandler

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

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

使用此配置,访问资源会使用自定义 . 当预订流结束时,将处理流执行结果并重定向到相应的控制器。/hotels/bookinghotels/bookingBookingFlowHandlerFlowHandlerspring-doc.cn

19.3.3. 重定向FlowHandler

处理 a 的 a 或返回 a 以指示处理后要重定向到的资源。 在上一节所示的示例中,重定向到结果的资源 URI 和所有其他结果的资源 URI。FlowHandlerFlowExecutionOutcomeFlowExceptionStringBookingFlowHandlerbooking/showbookingConfirmedhotels/indexspring-doc.cn

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

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

  • servletRelative::重定向到相对于当前 Servlet 的资源spring-doc.cn

  • contextRelative::重定向到相对于当前 Web 应用程序上下文路径的资源spring-doc.cn

  • serverRelative::重定向到相对于服务器根的资源spring-doc.cn

  • http://或:重定向到完全限定的资源 URIhttps://spring-doc.cn

当您将指令与 a 或 an 结合使用时,流定义中也支持这些相同的重定向前缀,例如,.externalRedirect:view-stateend-stateview="externalRedirect:https://springframework.org"spring-doc.cn

19.4. 视图分辨率

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

19.5. 从 View 发出事件信号

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

19.5.1. 使用命名的 HTML 按钮来向事件发出信号

以下示例显示了同一表单上的两个按钮,分别在单击时发出信号和事件:proceedcancelspring-doc.cn

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

当按钮被按下时,Web Flow 会找到一个以 开头的请求参数名称,并将剩余的子字符串视为事件 ID。 所以在这个例子中,submitting 变成了 . 当有多个不同的事件可以从同一表单发出信号时,应考虑此样式。_eventId_\_eventId_proceedproceedspring-doc.cn

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

以下示例显示了在提交时向事件发出信号的表单:proceedspring-doc.cn

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

在这里,Web Flow 会检测特殊参数,并使用其值作为事件 ID。 仅当表单上有一个事件可以发出信号时,才应考虑此样式。\_eventIdspring-doc.cn

以下示例显示了一个链接,该链接在激活时向事件发出信号:cancelspring-doc.cn

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

触发事件会导致 HTTP 请求被发送回服务器。 在服务器端,流处理从其当前 . 这个解码过程的工作原理特定于视图实现。 回想一下,Spring MVC 视图实现会查找名为 . 如果未找到参数,则视图将查找以 URL 开头的参数,并使用剩余的子字符串作为事件 ID。 如果两个案例都不存在,则不会触发流事件。view-state_eventId\_eventId\_eventId_spring-doc.cn

19.6. 在页面上嵌入流

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

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

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

/hotels/booking?mode=embedded

在“页面嵌入”模式下启动时,流不会在 Ajax 请求期间发出流执行重定向。 只需在启动流时传递该参数。 您唯一关心的其他问题是使用 Ajax 请求,并仅呈现更新显示流的页面部分所需的内容。mode=embeddedspring-doc.cn

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

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

19.6.2. 嵌入式流示例

有关嵌入在页面和模式对话框中的流的示例,请参阅项目。 您可以在本地签出源代码,像 Maven 项目一样构建它,然后将其导入到 Eclipse 中,如下所示:webflow-showcasespring-doc.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 Flash Scope

您可以在执行内部重定向时自动将流输出保存到 MVC 闪存范围。 这在流程结束时显示摘要屏幕时特别有用。 为了向后兼容,默认情况下禁用此功能。 要启用它,请在 设置为 ,如下所示:end-statesaveOutputToFlashScopeOnRedirectFlowHandlerAdaptertruespring-doc.cn

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

以下示例在重定向到屏幕之前添加到 MVC 闪存范围。confirmationNumbersummaryspring-doc.cn

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