Spring MVC源码和设计思想3 拦截器HandlerInterceptor

Spring MVC源码和设计思想3 拦截器HandlerInterceptor

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


2019-06-09鱼鱼

{{blog.title}}

创建于 {{blog.createTimeStr}}   created  by  {{blog.author}} {{tag}}
最后修改于 {{blog.timelineStr}}
修改文档