看完springboot拦截器源码,发现百万阅读文章竟是错误的

前言

在研究拦截器源码的时候看到一篇百万阅读的文章,看完源码发现这篇文章有个点不太对。

这里给大家框出来了,看看哪一句不对劲呢? 注:我看的源码springboot版本为2.2.10.RELEASE

例子

创建2个拦截器,按顺序添加进拦截链中。

public class FirstInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor 执行 preHandle--------------------");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("FirstInterceptor 执行 postHandle--------------------");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("FirstInterceptor 执行 afterCompletion--------------------");
    }
}                        

public class SecondInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SecondInterceptor 执行 preHandle--------------------");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("SecondInterceptor 执行 postHandle--------------------");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("SecondInterceptor 执行 afterCompletion--------------------");
    }
}

添加进链:

@Configuration
public class WebConfiguer extends WebMvcConfigurationSupport {

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new FirstInterceptor());
        registry.addInterceptor(new SecondInterceptor());
    }
}

随意写一个接口进行调用,查看日志:

可以归纳为先进后出的策略。 先进指的是preHandle按拦截器顺序执行,其他按倒序执行。

为什么?

经过debug之后找到了源码执行的地方。

从图中圈出来的点可以清晰的看到,拦截器在什么时候会调用。

preHandle的执行

源码中是先调用applyPreHandle()方法,执行拦截器的前置拦截,再看下前置如何处理的。

其实就是遍历所有的拦截器一一调用前置方法,如果返回false则再触发afterCompletion拦截方法。

呐呐,源码就在这里写着了,开头那篇文章怎么说的

返回false不会触发afterCompletion。

目前看这是不完全对的。为什么呢? 在某些条件下确实是执行不了afterCompletion的,这里要注意后面一段代码。

this.interceptorIndex = i

这里将已经执行的过位置赋值给了这个变量,这个变量实际上是会影响afterCompletion的执行。

后面看afterCompletion的实现时我们再讲。

postHandle的执行

可以看到源码中第三个框的地方,这里就是后置拦截postHandle的执行位置。

这里可以看到在遍历的时候是先从最后一个开始的,然后i--依次执行,这里也就解释了为什么后置拦截postHandle是倒序执行了。 但是为什么要这样设计呢? 我找了下作者的备注:

* DispatcherServlet processes a handler in an execution chain, consisting
* of any number of interceptors, with the handler itself at the end.
* With this method, each interceptor can post-process an execution,
* getting applied in inverse order of the execution chain

default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }

翻译之后也只是说使用这个方法可以进行后处理,以执行链相反的顺序执行,并没有解释说为什么这样设计,大家有兴趣可以继续深挖一下。

afterCompletion的执行

现在我们知道了前置和后置是怎么执行的了,那么afterCompletion在什么时候调用呢?

细心一下看源码图其实可以看到最后一个红框的地方,在这里就是afterCompletion执行的位置。

看一下它的内部代码怎么实现的:

这里有一点不同,这边是按照interceptiorIndex的位置开始执行的。

在前面preHandle的时候我们注意到了这个变量,它决定afterCompletion能不能运行。

正常情况下,所有的拦截器都运行了preHandle那么这里的变量就是最后一个拦截器的位置。

假设是5个拦截器,那么正常这个变量就为4,再按照倒序执行afterCompletion方法。

假设第一个拦截器的preHandle成功,第二个返回false了,那么这里还是能执行第一个拦截器的afterCompletion方法的。

所以说上面那篇百万阅读的文章不完全正确,有兴趣可以自己验证一下,这里我贴一下我验证的结果:

第一个拦截器成功执行了afterCompletion方法,至此破案了。

总结

拦截器是spring提供的一种对于请求的前后及完成状态的钩子,用于开发自定义处理。

它有3个常用的钩子,分别是:

  • preHandle:请求处理之前的钩子,按拦截器顺序执行。
  • postHandle:请求完成之后的钩子,按拦截器倒序执行。
  • afterCompletion:原本用于视图渲染之后调用的,现在来说用于请求不管是否异常后的清场工作。,倒序执行。

注意:preHandle并不是返回false就一定不会触发afterCompletion钩子。

preHandle成功了几个就必然会执行几个afterCompletion,如果存在必须要执行的afterComletion方法的话一定要确保它前面和自己的拦截器成功执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值