[TOC]

前语

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

看完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());
    }
}

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

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

能够归纳为先进后出的策略。 先进指的是preHandle按阻拦器次序履行,其他按倒序履行。

为什么?

通过debug之后找到了源码履行的当地。

看完springboot拦截器源码,发现百万阅读文章竟是错误的
从图中圈出来的点能够明晰的看到,阻拦器在什么时候会调用。

preHandle的履行

源码中是先调用applyPreHandle()办法,履行阻拦器的前置阻拦,再看下前置怎样处理的。

看完springboot拦截器源码,发现百万阅读文章竟是错误的
其实便是遍历所有的阻拦器一一调用前置办法,假如回来false则再触发afterCompletion阻拦办法。

呐呐,源码就在这儿写着了,最初那篇文章怎样说的

回来false不会触发afterCompletion。

现在看这是不完全对的。为什么呢? 在某些条件下确实是履行不了afterCompletion的,这儿要留意后边一段代码。

this.interceptorIndex = i

这儿将已经履行的过方位赋值给了这个变量,这个变量实践上是会影响afterCompletion的履行。

后边看afterCompletion的完结时我们再讲。

postHandle的履行

能够看到源码中第三个框的当地,这儿便是后置阻拦postHandle的履行方位。

看完springboot拦截器源码,发现百万阅读文章竟是错误的
这儿能够看到在遍历的时候是先从最终一个开端的,然后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履行的方位。

看完springboot拦截器源码,发现百万阅读文章竟是错误的
看一下它的内部代码怎样完结的:
看完springboot拦截器源码,发现百万阅读文章竟是错误的
这儿有一点不同,这边是依照interceptiorIndex的方位开端履行的。

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

正常情况下,所有的阻拦器都运行了preHandle那么这儿的变量便是最终一个阻拦器的方位。

假设是5个阻拦器,那么正常这个变量就为4,再依照倒序履行afterCompletion办法。

假设第一个阻拦器的preHandle成功,第二个回来false了,那么这儿仍是能履行第一个阻拦器的afterCompletion办法的。

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

看完springboot拦截器源码,发现百万阅读文章竟是错误的
第一个阻拦器成功履行了afterCompletion办法,至此破案了。

总结

阻拦器是spring提供的一种关于恳求的前后及完结状况的钩子,用于开发自定义处理。

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

  • preHandle:恳求处理之前的钩子,按阻拦器次序履行。
  • postHandle:恳求完结之后的钩子,按阻拦器倒序履行。
  • afterCompletion:原本用于视图烘托之后调用的,现在来说用于恳求不管是否反常后的清场工作。,倒序履行。

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

preHandle成功了几个就必然会履行几个afterCompletion,假如存在必需要履行的afterComletion办法的话一定要保证它前面和自己的阻拦器成功履行。

ps:看啥文章都不如自己实践走一遍来的好,假如有协助还请三连。。。