Spring 的前几个版本,开发者都需要在 web 应用的上下文定义(多个) HandlerMapping
bean,用来将
web 请求映射到指定的 handler。可当引入注解控制器时,开发者基本不再需要这样配置。因为 RequestMappingHandlerMapping
会自动寻找所有 @Controller
bean
中的 @RequestMapping
注解。另外提醒一下,所有从 AbstractHandlerMapping
继承过来的 HandlerMapping
类,都以通过设置以下属性来自定义其行为:
interceptors
拦截器链。HandlerInterceptor
s 会在 Section 16.4.1, “使用 HandlerInterceptor 拦截请求” 谈论。defaultHandler
默认 handler。此 handler 不影响其他 handler 的使用。order
order 属性 (可查看org.springframework.core.Ordered
接口), Spring会对可匹配的 handler 进行排序,并应用第一个匹配到 handler。alwaysUseFullPath
当此属性为true
时,Spring 会使用当前 Servlet 上下文的全路径去寻找合适的 handler。当为false
时(默认值),Spring 会使用相对路径来寻找合适的 handler。举个例子,当 某个 Servlet 映射/testing/*
请求时,若alwaysUseFullPath
属性为true
,会使用/testing/viewPage.html
;反之,使用/viewPage.html
。urlDecode
从 Spring 2.5 开始,此属性默认为true
。如果你更需要编码路基路径,可将此属性设置为 true。然而,HttpServletRequest
总会暴露解码后的 Servlet 路径。另外注意的是,当比较编码后的路径时,Servlet 路径是不会再匹配的。
如下例子,演示了如何配置一个拦截器:
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <property name="interceptors"> <bean class="example.MyInterceptor"/> </property> </bean> <beans>
16.4.1 使用 HandlerInterceptor 拦截请求
Spring 的 handler 映射机制包含了 handler 拦截器。使用handler 拦截器,可以在某些的请求中应用的特殊的功能,比如说,检查权限。
handler 映射的拦截器必须实现 HandlerInterceptor
接口(此节接口位于 org.springframework
.web.servlet
包中)。这个接口定义了三个方法:preHandle(..)
在
handler 执行前调用;postHandle(..)
在handler
执行后调用;afterCompletion(..)
在整一个请求完成后调用。这三个方法基本足够应对各种预处理和后处理的状况。
preHandle(..)
方法返回一个
boolean 值。你可以使用这个方法来中断或继续处理 handler 执行链。当此方法返回 true
时,hadler
执行链会继续执行;反之,DispatcherServlet
会认为此拦截器已处理完成该请求(和渲染一个视图),之后不再执行余下的拦截器,也不在执行
handler 执行链。
可以使用 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; } }
通过这样的配置,所有请求 handler 都会被 TimeBasedAccessInterceptor
拦截。如果当前时间是下班时间,用户会重定向到一个静态页面,换句话说就是,你只能在上班时间访问该网站。
[提示]
当使用 RequestMappingHandlerMapping
时,真实的
handler 是一个HandlerMethod
实例,该实例指定了会被调用的控制器方法。
如你所见,Spring 的适配器类 HandlerInterceptorAdapter
,使继承 HandlerInterceptor
接口变得更加简单。
在上述例子中,所配置的拦截器会应用到所有带注解的请求处理器。如果需要缩窄拦截器的拦截 url 路径范围,可以使用 MVC 命名空间或 MVC Java 配置,或声明 |
注意,HandlerInterceptor
的 postHandle
方法不一定适用于@ResponseBody
和ResponseEntity
方法。在这种情况下,HttpMessageConverter
实例会在 postHandle
方法执行之前就将数据写到
response 并提交 response,所以 postHandle
方法不可能再处理
response(如添加一个 Header)。相反,应用程序可以实现 ResponseBodyAdvice
,将其声明为 @ControllerAdvice
bean
或将其直接在 RequestMappingHandlerAdapter
中配置它。