19. Spring MVC 集成
本章展示了如何将 Web Flow 集成到 Spring MVC Web 应用程序中。
这booking-mvc
示例应用程序是 Spring MVC with Web Flow 的一个很好的参考。
此应用程序是一个简化的旅游网站,允许用户搜索和预订酒店房间。
19.1. 配置web.xml
使用 Spring MVC 的第一步是配置DispatcherServlet
在web.xml
.
通常每个 Web 应用程序执行一次此作。
以下示例映射以/spring/
自DispatcherServlet
.
一init-param
用于提供contextConfigLocation
.
这是 Web 应用程序的配置文件。
<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
将应用程序资源的请求映射到处理程序。
流是一种类型的处理程序。
19.2.1. 注册FlowHandlerAdapter
将请求分派到流的第一步是在 Spring MVC 中启用流处理。
为此,请安装FlowHandlerAdapter
如下:
<!-- Enables FlowHandler URL mapping -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
<property name="flowExecutor" ref="flowExecutor" />
</bean>
19.2.2. 定义流映射
启用流处理后,下一步是将特定应用程序资源映射到流。
最简单的方法是定义FlowHandlerMapping
如下:
<!-- 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/booking
resource path 将导致对 ID 为hotels/booking
.
如果找到具有该 ID 的流,则该流将处理请求。
如果未找到流,则查询调度程序有序链中的下一个处理程序映射,或者noHandlerFound
返回响应。
19.2.3. 流处理工作流程
找到有效的流映射后,FlowHandlerAdapter
根据 HTTP 请求提供的信息确定是开始该流的新执行还是恢复现有执行。
适配器采用许多与启动和恢复流执行相关的默认值:
-
HTTP 请求参数在所有启动流执行的输入映射中可用。
-
当流执行结束而没有发送最终响应时,默认处理程序会尝试在同一请求中启动新的执行。
-
未处理的异常将传播到
Dispatcher
,除非例外是NoSuchFlowExecutionException
.默认处理程序尝试从NoSuchFlowExecutionException
通过重新开始新的执行。
请参阅API 文档FlowHandlerAdapter
了解更多信息。
您可以通过子类化或实现自己的FlowHandler
,如下一节所述。
19.3. 实现自定义流处理程序
FlowHandler
是可用于自定义流在 HTTP Servlet 环境中的使用方式的扩展点。
一个FlowHandler
由FlowHandlerAdapter
并负责:
-
返回
id
要调用的流定义 -
创建输入以在启动该流的新调用时传递它们
-
处理该流的调用在结束时返回的结果
-
在发生该流的调用引发的任何异常时处理它们
这些职责在org.springframework.mvc.servlet.FlowHandler
接口:
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
.
所有这些作都是可选的。如果不实现它们,则默认值适用。
只需覆盖所需的方法。
具体来说,您应该:
-
覆盖
getFlowId(HttpServletRequest)
当流的 ID 无法直接从 HTTP 请求派生时。默认情况下,要调用的流的 ID 派生自pathInfo
请求 URI 的一部分。例如http://localhost/app/hotels/booking?hotelId=1
导致流 ID 为hotels/booking
默认情况下。 -
覆盖
createExecutionInputMap(HttpServletRequest)
当您需要对从HttpServletRequest
.默认情况下,所有请求参数都被视为流输入参数。 -
覆盖
handleExecutionOutcome
当您需要以自定义方式处理特定的流执行结果时。默认行为会将重定向发送到已结束流的 URL,以重新启动流的新执行。 -
覆盖
handleException
当您需要对未处理的流异常进行细粒度控制时。当客户端尝试访问已结束或过期的流执行时,默认行为会尝试重新启动流。任何其他异常都会重新抛出给 Spring MVCExceptionResolver
基础设施。
19.3.1. 示例FlowHandler
Spring MVC 和 Web Flow 之间的常见交互模式是将流重定向到@Controller
当它结束时。FlowHandler
instances 允许在不将流定义本身与特定控制器 URL 耦合的情况下完成此作。
以下示例FlowHandler
重定向到 Spring MVC 控制器:
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
结果结果会导致重定向以显示新预订。
任何其他结果都会重定向回酒店的索引页面。
19.3.2. 部署自定义FlowHandler
安装自定义FlowHandler
,您需要将其部署为 bean。
Bean 名称必须与处理程序应应用的流的 ID 匹配。
以下示例创建一个与hotels/booking
流:
<bean name="hotels/booking" class="org.springframework.webflow.samples.booking.BookingFlowHandler" />
使用此配置,访问资源/hotels/booking
启动hotels/booking
使用自定义BookingFlowHandler
.
预订流结束后,FlowHandler
处理流执行结果并重定向到相应的控制器。
19.3.3.FlowHandler
重 定向
一个FlowHandler
处理FlowExecutionOutcome
或FlowException
返回一个String
指示处理后要重定向到的资源。
在上一节所示的示例中,BookingFlowHandler
重定向到booking/show
资源 URIbookingConfirmed
结果和hotels/index
资源 URI 用于所有其他结果。
默认情况下,返回的资源位置是相对于当前 servlet 映射的。 这允许流处理程序使用相对路径重定向到应用程序中的其他控制器。 此外,对于需要更多控制的情况,还支持显式重定向前缀。
支持的显式重定向前缀包括:
-
servletRelative:
:重定向到相对于当前 servlet 的资源 -
contextRelative:
:重定向到相对于当前 Web 应用程序上下文路径的资源 -
serverRelative:
:重定向到相对于服务器根目录的资源 -
http://
或https://
:重定向到完全限定的资源 URI
当您使用externalRedirect:
指令与view-state
或end-state
——例如,view="externalRedirect:https://springframework.org"
.
19.4. 视图分辨率
除非另有说明,否则 Web Flow 会将选定的视图标识符映射到位于流工作目录中的文件。
对于现有的 Spring MVC 和 Web Flow 应用程序,外部ViewResolver
可能已经为您处理了此映射。
因此,要继续使用该解析器并避免更改现有流视图的打包方式,您可以按如下方式配置 Web 流:
<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
通过将useSpringBinding
flag 到true
.
这是使用统一 EL 进行视图到模型数据绑定的替代方法。
有关更多信息,请参阅此类的 JavaDoc API。
19.5. 从视图发出事件信号
当流进入view-state
,它会暂停,将用户重定向到其执行 URL,并等待用户事件恢复。
事件通常通过激活按钮、链接或其他用户界面命令来发出信号。
服务器端如何解码事件特定于正在使用的视图技术。
本节介绍如何从模板引擎(如 JSP、Velocity 或 Freemarker)生成的基于 HTML 的视图触发事件。
19.5.1. 使用命名的 HTML 按钮来指示事件
以下示例显示了同一窗体上的两个按钮,该按钮发出信号proceed
和cancel
单击时的事件:
<input type="submit" name="_eventId_proceed" value="Proceed" />
<input type="submit" name="_eventId_cancel" value="Cancel" />
按下按钮时,Web Flow 会查找以_eventId_
并将剩余的子字符串视为事件 ID。
因此,在此示例中,提交\_eventId_proceed
成为proceed
.
当有多个不同的事件可以从同一形式发出信号时,应考虑这种样式。
19.6. 在页面上嵌入流
默认情况下,当流进入视图状态时,它会在呈现视图之前执行客户端重定向。
这种方法称为POST-REDIRECT-GET
.
它的优点是将一个视图的表单处理与下一个视图的呈现分开。
因此,浏览器的“后退”和“刷新”按钮可以无缝工作,而不会引起任何浏览器警告。
通常,从用户的角度来看,客户端重定向是透明的。
但是,在某些情况下POST-REDIRECT-GET
可能不会带来同样的好处。
例如,流可能嵌入在页面上,并由 Ajax 请求驱动,仅刷新属于该流的页面区域。
在这种情况下,不仅没有必要使用客户端重定向,而且在保持页面周围内容完整方面也不是所需的行为。
处理 Ajax 请求 部分介绍了如何在 Ajax 请求期间进行部分渲染。 本节的重点是解释如何在 Ajax 请求期间控制流执行重定向行为。 若要指示流应在“页面嵌入”模式下运行,请在启动流时附加一个额外的参数,如下所示:
/hotels/booking?mode=embedded
在“页面嵌入”模式下启动时,流不会在 Ajax 请求期间发出流执行重定向。
这mode=embedded
参数仅在启动流时才需要传递。
您唯一需要关注的是使用 Ajax 请求,并且仅呈现更新显示流的页面部分所需的内容。
19.7. 保存流输出到 MVC 闪存示波器
当end-state
执行内部重定向。
这在流结束时显示摘要屏幕时特别有用。
为了向后兼容,此功能默认处于禁用状态。
要启用它,请将saveOutputToFlashScopeOnRedirect
在您的FlowHandlerAdapter
自true
如下:
<!-- 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
屏幕。
<end-state id="finish" view="externalRedirect:summary">
<output name="confirmationNumber" value="booking.confirmationNumber" />
</end-state>