拦截器和过滤器区别

2021-08-22/2021-08-22
0 评论 165 浏览

参考文档:

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

1. 实现原理不同

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

这里重点说下过滤器!

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

1public interface FilterChain {
2    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
3}

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

 1public final class ApplicationFilterChain implements FilterChain {
 2    @Override
 3    public void doFilter(ServletRequest request, ServletResponse response) {
 4            ...//省略
 5            internalDoFilter(request,response);
 6    }
 7 
 8    private void internalDoFilter(ServletRequest request, ServletResponse response){
 9    if (pos < n) {
10            //获取第pos个filter    
11            ApplicationFilterConfig filterConfig = filters[pos++];        
12            Filter filter = filterConfig.getFilter();
13            ...
14            filter.doFilter(request, response, this);
15        }
16    }
17}

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

2. 使用范围不同

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

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

3. 触发时机不同

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

v2-66c2ba948283c44f9e07d6af8933caf9_1440w3. 触发时机不同

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

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

执行顺序为:

1Filter 处理中 (chain.doFilter 前)
2Interceptor 前置 (preHandle)
3Controller
4Interceptor 处理中 (postHandle)
5Interceptor 后置 (afterCompletion)
6Filter 处理中 (chain.doFilter 后)

4. 请求范围不同

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

5. 注入 Bean 情况不同

在实际的业务场景中,应用到过滤器或拦截器,为处理业务逻辑难免会引入一些service服务。

下边我们分别在过滤器和拦截器中都注入service,看看有什么不同?

1@Component
2public class TestServiceImpl implements TestService {
3
4    @Override
5    public void a() {
6        System.out.println("我是方法A");
7    }
8}

过滤器中注入service,发起请求测试一下 ,日志正常打印出“我是方法A”。

 1@Autowired
 2    private TestService testService;
 3
 4    @Override
 5    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 6
 7        System.out.println("Filter 处理中");
 8        testService.a();
 9        filterChain.doFilter(servletRequest, servletResponse);
10    }
11Filter 处理中
12我是方法A
13Interceptor 前置
14我是controller
15Interceptor 处理中
16Interceptor 后置

在拦截器中注入service,发起请求测试一下 ,竟然TM的报错了,debug跟一下发现注入的service怎么是Null啊?

v2-a2c510a0ce979e35b90033599e900dab_1440w

这是因为加载顺序导致的问题,拦截器加载的时间点在 springcontext 之前,而 Bean 又是由 spring 进行管理。

解决方案也很简单,我们在注册拦截器之前,先将Interceptor 手动进行注入。注意:在registry.addInterceptor()注册的是getMyInterceptor() 实例。

 1@Configuration
 2public class MyMvcConfig implements WebMvcConfigurer {
 3
 4    @Bean
 5    public MyInterceptor getMyInterceptor(){
 6        System.out.println("注入了MyInterceptor");
 7        return new MyInterceptor();
 8    }
 9 
10    @Override
11    public void addInterceptors(InterceptorRegistry registry) {
12
13        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");
14    }
15}

6. 控制执行顺序

过滤器

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

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

1@Order(Ordered.HIGHEST_PRECEDENCE)
2@Component
3public class MyFilter2 implements Filter {}

拦截器

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

1@Override
2public void addInterceptors(InterceptorRegistry registry) {
3	registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2);
4	registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").order(1);
5	registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(3);
6}

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

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

 1Interceptor1 前置
 2Interceptor2 前置
 3Interceptor 前置
 4我是controller
 5Interceptor 处理中
 6Interceptor2 处理中
 7Interceptor1 处理中
 8Interceptor 后置
 9Interceptor2 处理后
10Interceptor1 处理后

标题:拦截器和过滤器区别
作者:Rainsheep
地址:HTTPS://www.rainsheep.cn/articles/2021/08/22/1629608544693.html

评论
发表评论
       
       
取消