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