系列的源码基于Java Spring 框架5.1.x版本。
HandlerInterceptor
HandlerInterceptor是SpringMVC框架提供的独有拦截器,本身只是一个接口,提供了三个方法,方法作用情况我已标出:
public interface HandlerInterceptor {
//前置处理,执行ha.handle方法之前执行,方法若返回false,请求不过控制层
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
//后置处理,执行ha.handle方法执行成功之后(返回true)执行
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
//请求整体完成后执行,异常也会执行,同样是在相应的preHandle返回true时才会执行
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
有关方法执行的具体时机,可以参考Spring MVC源码和设计思想1 DispatcherServlet文中的代码。
tip:default关键字
上面使用到了default关键字,default关键字是Java 8的新特性之一(之前只有用在switch中),通过default可以在接口中定义一个方法的方法体,从而使该方法不必被强制继承。Java8中也添加了static用于修饰接口方法。主要是为了考虑接口重复方法的设计,比如多个类继承与同一个接口并且需要定义相同的方法实现时,用过default或static可以避免产生重复代码。注意此处的default不是指权限修饰符中的默认值
default使用不恰当也会产生问题,比如继承了多个接口中拥有同样得default方法签名(即方法名和参数列表),如果不做重写处理编译器会报错。但无论如何,能利用好这些新特性绝对是一件锦上添花的事情。
方法的调用-HandlerExecutionChain
回顾一下在doDispatch方法中,我们通过HandlerExecutionChain的相关方法串联实现了拦截器的相关功能,接下来逐一分解。
前-applyPreHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//循环中按顺序执行前置方法
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
//当执行结果为false将直接跳出(跳出前会执行after)
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
//记录了拦截器执行的数量,中断后面的拦截器
//该值初始值为-1
this.interceptorIndex = i;
}
}
return true;
}
在执行preHandle方法返回false后,将直接跳过postHandle的执行,这一点请注意。为了保证afterCompletion与preHandle一一对应,此处记录了执行索引,防止后面不该执行的拦截器被执行到,而只执行前面返回值为true部分的拦截器。
后-applyPostHandle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
此时已经返回了ModalAndView对象,注意这里的interceptor的循环式倒序的,与前置的preHandle方法执行顺序恰巧相反。
over-triggerAfterCompletion
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//利用到了applyPreHandle中记录到的索引值,保证执行到的都是返回值为true的方法
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
//单个处理失败,打印日志,但不影响后面继续循环
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
同applyPostHandle,该方法也是在方法执行后倒序执行的。

