处理程序映射
简介
在早期版本的 Spring 中,用户需要在 Web 应用程序上下文中定义一个或多个 HandlerMapping
bean 以将传入的 Web 请求映射到适当的处理程序。随着注解控制器的引入,通常不再需要这样做,因为 RequestMappingHandlerMapping
会自动查找所有 @Controller
bean 上的 @RequestMapping
注解。然而,请记住,所有从 AbstractHandlerMapping
扩展的 HandlerMapping
类都具有以下属性,您可以使用这些属性来定制它们的行为:
- interceptors:拦截器列表。
HandlerInterceptors
在第 22.4.1 节“使用HandlerInterceptor
拦截请求”中讨论。 - defaultHandler:当此处理程序映射未能找到匹配的处理程序时使用的默认处理程序。
- order:基于
order
属性的值(参见org.springframework.core.Ordered
接口),Spring 会对上下文中可用的所有处理程序映射进行排序,并应用第一个匹配的处理程序。 - alwaysUseFullPath:如果设置为
true
,Spring 将使用当前 Servlet 上下文中的完整路径来查找适当的处理程序。如果设置为false
(默认值),则使用当前 Servlet 映射中的路径。例如,如果一个 Servlet 使用/testing/*
进行映射,并且alwaysUseFullPath
属性设置为true
,则使用/testing/viewPage.html
,而如果属性设置为false
,则使用/viewPage.html
。 - urlDecode:默认为
true
,从 Spring 2.5 开始。如果您更喜欢比较编码路径,请将此标志设置为false
。然而,HttpServletRequest
始终以解码形式公开 Servlet 路径。请注意,当与编码路径进行比较时,Servlet 路径将不匹配。
以下示例展示了如何配置一个拦截器:
<beans> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <property name="interceptors"> <bean class="example.MyInterceptor"/> </property> </bean> </beans>
<beans>
标签用于定义 Spring 应用上下文的配置文件。<bean>
标签用于定义一个 Spring bean。这里定义了一个RequestMappingHandlerMapping
bean,它用于处理基于注解的控制器请求映射。<property name="interceptors">
标签用于设置RequestMappingHandlerMapping
bean 的interceptors
属性,这个属性是一个拦截器列表。<bean class="example.MyInterceptor"/>
定义了一个名为example.MyInterceptor
的拦截器 bean,它会被添加到RequestMappingHandlerMapping
的拦截器列表中。
通过这样的配置,example.MyInterceptor
将作为拦截器用于处理所有匹配的请求。
Intercepting requests with a HandlerInterceptor
使用 HandlerInterceptor 拦截请求
Spring 的处理器映射机制包括处理器拦截器,这在你希望对某些请求应用特定功能时非常有用,例如检查主体。
位于处理器映射中的拦截器必须实现 org.springframework.web.servlet
包中的 HandlerInterceptor
接口。此接口定义了三个方法:preHandle(..)
在实际处理器执行之前调用;postHandle(..)
在处理器执行后调用;afterCompletion(..)
在整个请求完成后调用。这三个方法应该提供足够的灵活性来进行各种预处理和后处理。
preHandle(..)
方法返回一个布尔值。你可以使用此方法来中断或继续执行链的处理。当此方法返回 true
时,处理器执行链将继续;当它返回 false
时,DispatcherServlet
假定拦截器本身已经处理了请求(例如,渲染了适当的视图),并且不会继续执行执行链中的其他拦截器和实际处理器。
可以使用 interceptors
属性配置拦截器,该属性存在于从 AbstractHandlerMapping
扩展的所有 HandlerMapping
类上。以下示例展示了如何配置:
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <property name="interceptors"> <list> <ref bean="officeHoursInterceptor"/> </list> </property> </bean> <bean id="officeHoursInterceptor" class="samples.TimeBasedAccessInterceptor"> <property name="openingTime" value="9"/> <property name="closingTime" value="18"/> </bean> </beans>
package samples; public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { private int openingTime; private int closingTime; public void setOpeningTime(int openingTime) { this.openingTime = openingTime; } public void setClosingTime(int closingTime) { this.closingTime = closingTime; } public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Calendar cal = Calendar.getInstance(); int hour = cal.get(HOUR_OF_DAY); if (openingTime <= hour && hour < closingTime) { return true; } response.sendRedirect("http://host.com/outsideOfficeHours.html"); return false; } }
在上述代码中,我们定义了一个 TimeBasedAccessInterceptor
拦截器,它会在处理任何请求之前检查当前时间是否在办公时间内。如果在办公时间内,请求将继续处理;否则,用户将被重定向到一个静态的 HTML 文件。
在这个配置中,任何由 RequestMappingHandlerMapping
处理的请求都会被 TimeBasedAccessInterceptor
拦截。如果当前时间在 openingTime
和 closingTime
之间,拦截器返回 true
,请求继续处理。否则,拦截器会重定向到一个静态的 HTML 文件,并返回 false
来中断请求处理。
注意事项
- 使用
RequestMappingHandlerMapping
时,实际处理器是一个HandlerMethod
实例,它标识将被调用的具体控制器方法。 HandlerInterceptorAdapter
类简化了扩展HandlerInterceptor
接口的工作。- 如果你想将拦截器应用于特定的 URL 路径,可以使用 MVC 命名空间或 MVC Java 配置,或者声明类型为
MappedInterceptor
的 bean 实例。
实现方法的局限性
HandlerInterceptor
的postHandle
方法并不总是适用于@ResponseBody
和ResponseEntity
方法。在这种情况下,HttpMessageConverter
会在postHandle
调用之前写入并提交响应,这使得修改响应(例如添加一个头部信息)变得不可能。- 可以实现
ResponseBodyAdvice
接口,并将其声明为@ControllerAdvice
bean,或直接在RequestMappingHandlerAdapter
上配置,以解决上述问题。