系列的源码基于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,该方法也是在方法执行后倒序执行的。