Spring拦截器HandlerInterceptor与Filter方法执行顺序探究

  1. 单个拦截器与Controller方法的执行顺序
  2. 多个拦截器与Controller方法的执行顺序
  3. Filter与拦截器方法的执行顺序
  4. 多个Filter之间的执行顺序
  5. 最后总结

单个拦截器与Controller方法的执行顺序

拦截器的preHandle方法在进入到Controller方法之前执行,且只有当方法返回true时才能进入到被拦截的Controller方法;如果该方法返回false,则被拦截的Controller方法不会执行,且拦截器中的postHandleafterCompletion均不不会执行。

当拦截器的preHandle方法返回true时,各方法调用顺序如下:
1.拦截器的preHandle方法
2.Controller方法
3.拦截器的postHandle方法
4.拦截器的afterCompletion方法

当拦截器的preHandle方法返回true时,如果Controller方法中抛出异常,则各方法调用顺序如下:
1.拦截器的preHandle方法
2.Controller方法
3.使用@ControllerAdvice注解标记的全局异常拦截器
4.拦截器的afterCompletion方法

注意: 当Controller方法抛出异常时,拦截器的postHandle方法是不会执行的。

多个拦截器与Controller方法的执行顺序

当存在多个拦截器时,拦截器的执行顺序与它们的注册顺序一致,如下:

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TokenInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");
    }
}

由于TokenInterceptor先于LogInterceptor注册,所以TokenInterceptor将先被访问,它们的方法访问顺序为:
1.TokenInterceptor.preHandle方法
2.LogInterceptor.preHandle方法
3.Controller方法
4.LogInterceptor.postHandle方法
5.TokenInterceptor.postHandle方法
6.LogInterceptor.afterCompletion方法
7.TokenInterceptor.afterCompletion方法

注意: 由于TokenInterceptor.preHandle方法最先被执行,如果该方法返回false时,则后面的拦截器方法和Controller方法都不会执行。

另外,当TokenInterceptor.preHandle方法返回true时,如果Controller方法抛出异常,则多个拦截器与Controller方法之间的执行顺序如下:
1.TokenInterceptor.preHandle方法
2.LogInterceptor.preHandle方法
3.Controller方法
4.使用@ControllerAdvice注解标记的全局异常拦截器
5.LogInterceptor.afterCompletion方法
6.TokenInterceptor.afterCompletion方法

Filter与拦截器方法的执行顺序

一言以蔽之,Filter的doFilter方法会在所有拦截器方法之前执行,假设同时存在过滤器AuthFilter和拦截器TokenInterceptor,他们的配置参数如下:

@WebFilter(filterName = "AuthFilter", urlPatterns = "/*")
public class AuthFilter implements Filter {}

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

则它们的方法执行顺序如下:
1.AuthFilter.doFilter方法
2.TokenInterceptor.preHandle方法
3.Controller方法
4.TokenInterceptor.postHandle方法
5.TokenInterceptor.afterCompletion方法

Filter与拦截器的作用及区别如下图所示:

  • Filter是Servlet规范中的组件,依赖servlet容器,拦截所有Servlet请求
  • 拦截器(Interceptor)是Spring组件,只拦截特定的路径

多个Filter之间的执行顺序

Filter的执行顺序取决于它们的注册方式:

场景1: 实现javax.servlet.Filter接口,在web.xml文件中注册Filter

当在web.xml文件中注册Filter时,Filter的执行顺序取决于他们的注册顺序。

场景2: 实现javax.servlet.Filter接口,使用注解@WebFilter定义Filter

当用注解@WebFilter定义Filter时,Filter的执行顺序取决于Filter的类名字母顺序。

场景3: 实现javax.servlet.Filter接口,在Spring环境中通过org.springframework.boot.web.servlet.FilterRegistrationBean注册

如下示例:

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<AuthFilter> authFilter() {
        FilterRegistrationBean<AuthFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setOrder(2);
        registrationBean.setFilter(new AuthFilter());
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<LogFilter> logFilter() {
        FilterRegistrationBean<LogFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setOrder(1);
        registrationBean.setFilter(new LogFilter());
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<MDCFilter> mdcFilter() {
        FilterRegistrationBean<MDCFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setOrder(0);
        registrationBean.setFilter(new MDCFilter());
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }
}

通过org.springframework.boot.web.servlet.FilterRegistrationBean注册Filter时,Filter的执行顺序取决于注册时通过setOrder()方法设置的属性值大小,值越小越先被执行。

场景4: 在Spring环境中,实现org.springframework.boot.web.servlet.filter.OrderedFilter接口

在Spring环境中实现org.springframework.boot.web.servlet.filter.OrderedFilter接口时,Filter的执行顺序取决于getOrder()方法的返回值,值越小越先被执行。

特别注意: 此时Filter必须注入到Spring IoC容器中才会生效(可以使用注解@Component实现)

场景5:在Spring环境中,继承org.springframework.web.filter.OncePerRequestFilter抽象类

在Spring环境中实现org.springframework.web.filter.OncePerRequestFilter抽象类时,Filter的执行顺序取决于类名字母顺序。

特别注意: 此时Filter必须使用注入到Spring IoC容器中才会生效(可以使用注解@Component实现)

最后总结

关于Spring拦截器方法,Filter方法,全局异常拦截器(使用注解@ControllerAdvice)方法,与Controller方法的执行顺序总结如下图所示:

场景1:Controller方法不会抛异常

场景2:Controller方法抛出异常

【参考】
过滤器Filter实现及执行顺序
spring boot 实现多个 interceptor 并指定顺序
spring HandlerInterceptor方法的调用顺序
Springboot过滤器Filter和拦截器Inteceptor详解及使用场景


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,在下面评论区告诉我^_^^_^