参考文档:

Spring 拦截器和过滤器的区别?

1. 实现原理不同

过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于 Java 的反射机制(动态代理)实现的。

这里重点说下过滤器!

在我们自定义的过滤器中都会实现一个 doFilter() 方法,这个方法有一个 FilterChain 参数,而实际上它是一个回调接口。ApplicationFilterChain 是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。

java
  • 01
  • 02
  • 03
public interface FilterChain { void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException; }

ApplicationFilterChain 里面能拿到我们自定义的 xxxFilter 类,在其内部回调方法 doFilter() 里调用各个自定义 xxxFilter 过滤器,并执行 doFilter() 方法。

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
public final class ApplicationFilterChain implements FilterChain { @Override public void doFilter(ServletRequest request, ServletResponse response) { ...//省略 internalDoFilter(request,response); } private void internalDoFilter(ServletRequest request, ServletResponse response){ if (pos < n) { //获取第pos个filter ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = filterConfig.getFilter(); ... filter.doFilter(request, response, this); } } }

而每个 xxxFilter 会先执行自身的 doFilter() 过滤逻辑,最后在执行结束前会执行 filterChain.doFilter(servletRequest, servletResponse),也就是回调 ApplicationFilterChain 的 doFilter() 方法,以此循环执行实现函数回调。

2. 使用范围不同

我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在 Servlet 规范中定义的,也就是说过滤器 Filter 的使用要依赖于Tomcat 等容器,导致它只能在 web 程序中使用。

而拦截器 (Interceptor) 它是一个 Spring 组件,并由 Spring 容器管理,并不依赖 Tomcat 等容器,是可以单独使用的。不仅能应用在 web 程序中,也可以用于 Application、Swing 等程序中。

3. 触发时机不同

过滤器 和 拦截器的触发时机也不同,我们看下边这张图。

3. 触发时机不同

过滤器 Filter 是在请求进入容器后,但在进入 servlet 之前进行预处理,请求结束是在 servlet 处理完以后。

拦截器 Interceptor 是在请求进入 servlet 后,在进入 Controller 之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。

执行顺序为:

  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
Filter 处理中 (chain.doFilter 前) Interceptor 前置 (preHandle) Controller Interceptor 处理中 (postHandle) Interceptor 后置 (afterCompletion) Filter 处理中 (chain.doFilter 后)

4. 请求范围不同

  • 过滤器 Filter 执行了两次,拦截器 Interceptor 只执行了一次。
  • 过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对 Controller 中请求或访问 static 目录下的资源请求起作用。

5. 过滤器实现方式

5.1 filter 注入容器

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
@Component // 加入容器会自动可用 @Slf4j public class MyTestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyTestFilter doFilter"); chain.doFilter(request, response); } @Override public void destroy() { Filter.super.destroy(); } }

5.2 FilterRegistrationBean

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
@Slf4j public class MyTestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyTestFilter doFilter"); chain.doFilter(request, response); } @Override public void destroy() { Filter.super.destroy(); } }
java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
@Configuration public class FilterConfig { @Bean public FilterRegistrationBean<MyTestFilter> testFilterRegistration() { FilterRegistrationBean<MyTestFilter> registration = new FilterRegistrationBean<>(new MyTestFilter()); registration.addUrlPatterns("/test/*"); registration.setName("testFilter"); registration.setOrder(1); return registration; } }

5.3 @WebFilter

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
@Slf4j @WebFilter(urlPatterns = "/test/*", filterName = "testFilter") public class MyTestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyTestFilter doFilter"); chain.doFilter(request, response); } @Override public void destroy() { Filter.super.destroy(); } }

6. 拦截器实现

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
public class MyTestInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("preHandle"); return true; } }
java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
@Configuration public class InterceptorConfig extends WebMvcConfigurationSupport { @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyTestInterceptor()).addPathPatterns("/test/**"); super.addInterceptors(registry); } }

7. 拦截器无法注入 bean 处理

参考 为什么你写的拦截器注入不了 Java bean?

如下代码:

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
@Component public class MyTestInterceptor extends HandlerInterceptorAdapter { @Autowired private ServiceA serviceA; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("preHandle"); serviceA.methodC(); return true; } }
java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
@Configuration public class InterceptorConfig extends WebMvcConfigurationSupport { @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyTestInterceptor()).addPathPatterns("/test/**"); super.addInterceptors(registry); } }

我们会发现拦截器注入的 serviceA 为 null. 因为我们添加拦截器的时候,使用的是 new, 这个拦截器并没有加入 spring 容器,无法注入。

解决方法

将拦截器交给 spring ioc 管理即可

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
@Configuration public class InterceptorConfig extends WebMvcConfigurationSupport { @Autowired private MyTestInterceptor myTestInterceptor; @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myTestInterceptor).addPathPatterns("/test/**"); super.addInterceptors(registry); } }

8. 控制执行顺序

过滤器

实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,不过,有时我们希望某个过滤器或拦截器能优先执行,就涉及到它们的执行顺序。

过滤器用 @Order 注解控制执行顺序,通过 @Order 控制过滤器的级别,值越小级别越高越先执行。

java
  • 01
  • 02
  • 03
@Order(Ordered.HIGHEST_PRECEDENCE) @Component public class MyFilter2 implements Filter {}

拦截器

拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2); registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").order(1); registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(3); }

看到输出结果发现,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。

postHandle() 方法被调用的顺序跟 preHandle() 居然是相反的!如果实际开发中严格要求执行顺序,那就需要特别注意这一点。

  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
Interceptor1 前置 Interceptor2 前置 Interceptor 前置 我是controller Interceptor 处理中 Interceptor2 处理中 Interceptor1 处理中 Interceptor 后置 Interceptor2 处理后 Interceptor1 处理后