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
中配置它。