源码分析Spring boot拦截器执行顺序

一、提出问题
  1. 项目中存在多个拦截器,那么他们的执行顺序是如何的?
  2. 如何设置拦截器执行顺序?
二、前期准备

项目结构:
在这里插入图片描述
主要代码如下,有拦截器 A、B、C,代码基本与下一致:

/**
 * 拦截器 A
 *
 * @author ouyang
 * @version 1.0
 * @date 2020/7/30 15:18
 **/
public class AInterceptor implements HandlerInterceptor {

    private final Logger logger = LoggerFactory.getLogger(AInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("拦截器 A preHandle 方法执行");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        logger.info("拦截器 A postHandle 方法执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        logger.info("拦截器 A afterCompletion 方法执行");
    }
}

配置拦截器类:

/**
 * 拦截器配置
 * @author ouyang
 * @version 1.0
 * @date 2020/7/30 15:19
 **/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AInterceptor())
                .addPathPatterns("/**");

        registry.addInterceptor(new BInterceptor())
                .addPathPatterns("/**");

        registry.addInterceptor(new CInterceptor())
                .addPathPatterns("/**");
    }
}
三、拦截器实际运行结果

首先启动项目,任意访问一个接口,控制台打印如下:

2020-07-31 21:43:55.891  INFO 4284 --- [nio-8080-exec-1] com.study.aop.AInterceptor               : 拦截器 A preHandle 方法执行
2020-07-31 21:43:55.891  INFO 4284 --- [nio-8080-exec-1] com.study.aop.BInterceptor               : 拦截器 B preHandle 方法执行
2020-07-31 21:43:55.891  INFO 4284 --- [nio-8080-exec-1] com.study.aop.CInterceptor               : 拦截器 C preHandle 方法执行
2020-07-31 21:43:55.936  INFO 4284 --- [nio-8080-exec-1] com.study.aop.CInterceptor               : 拦截器 C postHandle 方法执行
2020-07-31 21:43:55.936  INFO 4284 --- [nio-8080-exec-1] com.study.aop.BInterceptor               : 拦截器 B postHandle 方法执行
2020-07-31 21:43:55.936  INFO 4284 --- [nio-8080-exec-1] com.study.aop.AInterceptor               : 拦截器 A postHandle 方法执行
2020-07-31 21:43:55.936  INFO 4284 --- [nio-8080-exec-1] com.study.aop.CInterceptor               : 拦截器 C afterCompletion 方法执行
2020-07-31 21:43:55.936  INFO 4284 --- [nio-8080-exec-1] com.study.aop.BInterceptor               : 拦截器 B afterCompletion 方法执行
2020-07-31 21:43:55.936  INFO 4284 --- [nio-8080-exec-1] com.study.aop.AInterceptor               : 拦截器 A afterCompletion 方法执行

从中可以看出,拦截器 A,B,C 中 preHandle 执行顺序与 registry.addInterceptor() 一致,postHandleafterCompletion 反之。

接下来我们跟踪一下源码,分析为什么会是上述现象:
首先 WebMvcConfigurationSupport 类中 requestMappingHandlerMapping 方法会将配置好的拦截器通过 InterceptorRegistry 类中 getInterceptors() 方法加载到 RequestMappingHandlerMapping;另外在请求时,通过 DispatcherServlet 中的 doDispatch 方法进行调用拦截器

如下是 doDispatch 方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
					// 执行拦截器的全部 preHandle 方法
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    // 执行拦截器的全部 postHandle 方法
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
				
				// 执行拦截器的全部 afterCompletion 方法
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                // 异常后,执行拦截器的全部 afterCompletion 方法
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                // 异常后,执行拦截器的全部 afterCompletion 方法
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

首先我们先来看 mappedHandler.applyPreHandle() 方法实际方法:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }

        return true;
    }

我们可以看出 applyPreHandle 是按照 HandlerInterceptor[] interceptors = this.getInterceptors(); 顺序执行的,也就是说 默认(不设定拦截器顺序) 是按照 registry.addInterceptor() 的顺序执行的;
接下来我们看 mappedHandler.applyPostHandle() ,实际执行方法如下:

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.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);
            }
        }

    }

可以看出是与 applyPreHandle 正好相反的顺序执行的;mappedHandler.triggerAfterCompletion() 方法如下所示:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = this.interceptorIndex; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];

                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                } catch (Throwable var8) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
                }
            }
        }
    }

可以看出和 applyPostHandle 方法一致。

最后我们来看一下 InterceptorRegistry 类的 addInterceptorgetInterceptors 方法,源码如下:

public class InterceptorRegistry {
    private final List<InterceptorRegistration> registrations = new ArrayList();
    private static final Comparator<Object> INTERCEPTOR_ORDER_COMPARATOR;

    public InterceptorRegistry() {
    }

    public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
        InterceptorRegistration registration = new InterceptorRegistration(interceptor);
        this.registrations.add(registration);
        return registration;
    }

    public InterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor) {
        WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor);
        InterceptorRegistration registration = new InterceptorRegistration(adapted);
        this.registrations.add(registration);
        return registration;
    }

    protected List<Object> getInterceptors() {
        return (List)this.registrations.stream().sorted(INTERCEPTOR_ORDER_COMPARATOR).map(InterceptorRegistration::getInterceptor).collect(Collectors.toList());
    }

    static {
        INTERCEPTOR_ORDER_COMPARATOR = OrderComparator.INSTANCE.withSourceProvider((object) -> {
            if (object instanceof InterceptorRegistration) {
                InterceptorRegistration var10000 = (InterceptorRegistration)object;
                ((InterceptorRegistration)object).getClass();
                return var10000::getOrder;
            } else {
                return null;
            }
        });
    }
}

不难看出, addInterceptor 方法是将拦截器放到 List<InterceptorRegistration> registrations = new ArrayList(); 中,另外需要特别注意 getInterceptors 方法,有代码 this.registrations.stream().sorted(INTERCEPTOR_ORDER_COMPARATOR),我们也可以看出 registrations 按照 order 升序排序,其中 InterceptorRegistration 类默认的 private int order = 0; ,可以通过 order(int order) 方法设置顺序。

四、结论

根据实践和源码分析,当项目中存在多个拦截器,那么他们的执行顺序是根据方法而定的,拦截器所有的 preHandle 方法是按照拦截器的 order 升序执行的,如果 order 一致,则按照添加顺序执行,postHandleafterCompletion 方法执行顺序反之。
另外我们可以通过使用 order(int order) 设定执行的先后顺序,如下代码所示。

@Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AInterceptor())
                .addPathPatterns("/**")
                .order(3);

        registry.addInterceptor(new BInterceptor())
                .addPathPatterns("/**")
                .order(2);

        registry.addInterceptor(new CInterceptor())
                .addPathPatterns("/**")
                .order(1);
    }

综上所述,默认情况下(不设定拦截器顺序),preHandle 方法执行顺序与拦截器注册时顺序一致,postHandleafterCompletion 方法执行顺序反之;如果我们给每个拦截器设定 order ,则 preHandle 方法执行顺序是按照 order 升序执行,postHandleafterCompletion 方法执行顺序反之。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lytao123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值