拦截器常用于在请求处理的不同阶段插入自定义逻辑。Spring MVC的拦截器作用是在请求到达控制器之前或之后进行拦截,可以对请求和响应进行一些特定的处理。如:
关于过滤器可以看我之前的文章过滤器Filter的介绍和使用。
我们很容易发现拦截器和过滤器十分相似,他们都是对某一阶段的前后进行拦截,进行一些处理。那么他们之间有什么不同呢?
过滤器(Filter)是servlet中定义的,而拦截器(HandlerInterceptor)则是由Spring MVC框架提供
二者所作用的范围不同
Spring MVC 提供了 HandlerInterceptor
接口,开发者可以通过实现这个接口来创建自定义的拦截器。其中定义了三个默认方法,用于对不同阶段进行拦截:
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
:
true
表示继续执行后续的拦截器和控制器方法;返回 false
表示中断执行,不再调用后续的拦截器和控制器方法。postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
:
ModelAndView
对象,添加额外的数据等。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
:
创建自定义拦截器:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在控制器方法执行前调用
System.out.println("preHandle..." );
//这里我们直接返回true
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在控制器方法执行后,但在视图渲染前调用
System.out.println("postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在整个请求处理完成后调用
System.out.println("AfterCompletion");
}
}
此时要想要该拦截器生效,我们还需在spring mvc配置文件中进行配置(默认对所有控制器进行拦截)
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/test"/>
<bean class="com.example.MyInterceptor"/>
mvc:interceptor>
mvc:interceptors>
也可以通过在自定义 拦截器的类上加上@component注解,此时的配置文件为
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/test"/>
<ref bean="myInterceptor">ref>
mvc:interceptor>
mvc:interceptors>
在 Spring MVC 中,多个拦截器可以组成一个拦截器链,按照注册(配置)顺序依次执行。假设现在按顺序注册三个拦截器Interceptor1,Interceptor2,Interceptor3。
为什么是这样的顺序呢?我们观察源码可以发现:
preHandle源码分析
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//interceptorList是一个ArrayList集合,按顺序存放了所有的拦截器
//下标从0开始,从这里我们可以知道为什么是顺序执行的。
//this.interceptorIndex = i++,注意这个代码,如果返回false,则它的值则表示当前返回false的拦截器的下标
for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
//如果返回false
if (!interceptor.preHandle(request, response, this.handler)) {
//执行AfterCompletion,这里我们就知道为什么不执行postHandle,而执行AfterCompletion了
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
return true;
}
postHandle源码分析
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
//可以看到这里是从最后一个拦截器开始逆序遍历
for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
afterCompletion源码分析
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
//this.interceptorList.size() - 1表示当前返回false的拦截器的上一个的下标
//注意这里是--i
//这也就解释了为什么是返回false的拦截器之前的拦截器逆序执行
for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}