19. Spring MVC 集成
本章介绍如何将 Web Flow 集成到 Spring MVC Web 应用程序中。
这booking-mvc
示例应用程序是带有 Web Flow 的 Spring MVC 的良好参考。
此应用程序是一个简化的旅游网站,允许用户搜索和预订酒店房间。
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. 分派到 Flows
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
response。
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
部分。例如http://localhost/app/hotels/booking?hotelId=1
导致流 ID 为hotels/booking
默认情况下。 -
覆盖
createExecutionInputMap(HttpServletRequest)
当您需要精细控制从HttpServletRequest
.默认情况下,所有请求参数都被视为流输入参数。 -
覆盖
handleExecutionOutcome
当您需要以自定义方式处理特定的流程执行结果时。默认行为将重定向发送到已结束流的 URL,以重新启动流的新执行。 -
覆盖
handleException
当您需要对未处理的流异常进行精细控制时。默认行为会在客户端尝试访问已结束或过期的流执行时重新启动流。任何其他异常都会重新抛出给 Spring MVCExceptionResolver
infrastructure 中。
19.3.1. 示例FlowHandler
Spring MVC 和 Web Flow 之间的一种常见交互模式是,流重定向到@Controller
当它结束时。FlowHandler
实例允许这样做,而无需将流定义本身与特定控制器 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
result 会导致重定向以显示新预订。
任何其他结果都会重定向回酒店的索引页面。
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
flow 使用自定义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 流会将选定的视图标识符映射到位于流的工作目录中的文件。
对于现有的 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 MVC 视图系统在 Spring Web Flow 中的使用方式的工厂。
您可以使用它来配置现有的ViewResolver
实例以及其他服务(例如自定义MessageCodesResolver
.
您还可以让数据绑定使用 Spring MVC 的原生BeanWrapper
通过设置useSpringBinding
flag 设置为true
.
这是使用 Unified EL 进行 View 到 Model 数据绑定的替代方法。
有关更多信息,请参阅此类的 JavaDoc API。
19.5. 从 View 发出事件信号
当流程进入view-state
,它会暂停,将用户重定向到其执行 URL,并等待 user 事件恢复。
事件通常通过激活按钮、链接或其他用户界面命令来发出信号。
服务器端如何解码事件特定于所使用的视图技术。
本节介绍如何从模板引擎(如 JSP、Velocity 或 Freemarker)生成的基于 HTML 的视图触发事件。
19.5.1. 使用命名的 HTML 按钮来向事件发出信号
以下示例显示了同一表单上的两个按钮,这两个按钮表示proceed
和cancel
events 分别:
<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
.
它的优点是将一个视图的表单处理与下一个视图的呈现分开。
因此,浏览器的 Back (返回) 和 Refresh (刷新) 按钮可以无缝工作,而不会引起任何浏览器警告。
通常,从用户的角度来看,客户端重定向是透明的。
但是,在某些情况下POST-REDIRECT-GET
可能不会带来相同的好处。
例如,流可能嵌入在页面上,并由 Ajax 请求驱动,仅刷新属于该流的页面区域。
在这种情况下,不仅没有必要使用客户端重定向,而且在保持页面周围内容完好无损方面也不是理想的行为。
处理 Ajax 请求部分介绍了如何在 Ajax 请求期间进行部分渲染。 本节的重点是说明如何在 Ajax 请求期间控制流执行重定向行为。 要指示流程应在“页面嵌入”模式下运行,请在启动流程时附加一个额外的参数,如下所示:
/hotels/booking?mode=embedded
在“页面嵌入”模式下启动时,流不会在 Ajax 请求期间发出流执行重定向。
这mode=embedded
参数,则只需在启动流时传递。
您唯一关心的其他问题是使用 Ajax 请求,并仅呈现更新显示流的页面部分所需的内容。
19.7. 将流输出保存到 MVC Flash Scope
当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 Flash 范围,然后重定向到summary
屏幕。
<end-state id="finish" view="externalRedirect:summary">
<output name="confirmationNumber" value="booking.confirmationNumber" />
</end-state>