Filter和Interceptor区别

174 阅读5分钟

Filter过滤器 Interceptor拦截器

1.过滤器和拦截器的区别

1.过滤器和拦截器底层实现方式不同

过滤器是基于函数回调,拦截器则是基于Java的反射机制(动态代理)实现的。

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

ApplicationFilterChain是他的实现类,这个实现类内部也有一个doFIlter()方法就是回调方法。

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

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

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()方法,也就是回调ApplicationFilterChain的doFilter()方法,以此循环执行实现函数回调

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        filterChain.doFilter(servletRequest, servletResponse);
    }

2.使用范围不同

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

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

3.触发时机不同

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

4.拦截的请求范围不同

同时配置过滤器和拦截器

@Controller 
@RequestMapping() 
public class Test {
    @RequestMapping("/test1")
    @ResponseBody
    public String test1(String a) {
        System.out.println("我是controller");
        return null;
    }
}

项目启动过程中发现,过滤器的init()方法,随着容器的启动进行了初始化

控制台打印日志如下

执行顺序:Filter init --Filter处理中(doFilter) -- Interceptor前置(preHanlder) -- 我是Controller(controller) -- Interceptor(postHanlder) -- Interceptor处理后

5.注入Bean情况不同

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

@Component
public class TestServiceImpl implements TestService {

    @Override
    public void a() {
        System.out.println("我是方法A");
    }
}

过滤器中注入service,发起请求测试,入职正常打印出我是方法A

@Autowired
    private TestService testService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("Filter 处理中");
        testService.a();
        filterChain.doFilter(servletRequest, servletResponse);
    }

打印结果

Filter 处理中
我是方法A
Interceptor 前置
我是controller
Interceptor 处理中
Interceptor 后置

在拦截器中注入service,为null

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

拦截器:老子今天要进洞房;
Spring:兄弟别闹,你媳妇我还没生出来呢!

解决方法:在注册拦截器之前,先将Interceptor手动进行注入。

注意:在registry.addInterceptor()注册的是getMyInterceptor()实例

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Bean
    public MyInterceptor getMyInterceptor(){
        System.out.println("注入了MyInterceptor");
        return new MyInterceptor();
    }
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
		//此时注册的是 getMyInterceptor实例  并添加拦截路径
        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");
    }
}

6.控制顺序不同

实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,我们希望过滤器或者拦截器或拦截器能按照自定义顺序执行

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

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

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

 @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()方法先执行,而postHandler()方法反而会后执行。

2.测试demo

1.myconfig

package com.web.config;
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {


    @Bean
    public MyFilter getFilter(){

        System.out.println("注入过滤器");
        return new MyFilter();
    }
    @Bean
    public MyInterceptor getInterceptor(){
        System.out.println("注入拦截器");
        return new MyInterceptor();
    }
    @Bean
    public MyInterceptor1 getInterceptor1(){
        System.out.println("注入拦截器1");
        return new MyInterceptor1();
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor( getInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(getInterceptor1()).addPathPatterns("/**");
    }
}

2.MyAppContext

public class MyAppContext implements ApplicationContextAware {
	//静态变量获取bean对象
    static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        MyAppContext.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> aClass) throws BeansException {
        return MyAppContext.applicationContext.getBean(aClass);
    }
}

3.MyFilter

public class MyFilter implements Filter {

    @Resource
    Myservice myservice;

    //项目启动加载
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter:前置设置");

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter:处理中");
        myservice.test();
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("Filter:后置设置");
    }
}

4.MyInterceptor

@Order(1)
public class MyInterceptor implements HandlerInterceptor {

    @Resource
    Myservice myservice;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        myservice.test();
        System.out.println("Interceptor: 处理");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor: 处理modleandview");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("释放资源");
    }
}
@Order(2)
public class MyInterceptor1 implements HandlerInterceptor {

    @Resource
    Myservice myservice;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        myservice.test();
        System.out.println("Interceptor1: 处理");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor1: 处理modleandview");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("1释放资源");
    }
}

5.service,impl,controller

public interface Myservice {

    public void test();

}
@Service
public class MyServiceImpl implements Myservice {
    int a = 1;
    @Override
    public void test() {

        System.out.println("执行方法第" + a + "次");

        a ++ ;
    }
}
@RequestMapping("/a")
@RestController
public class MyController {


    @GetMapping("/a")
    public void test(){
        System.out.println("myController");
    }
}

6.运行结果

2022-06-29 10:38:33.149  INFO 9316 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1648 ms
注入过滤器
Filter:前置设置
注入拦截器
注入拦截器1
2022-06-29 10:38:33.334  INFO 9316 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
Filter:处理中
执行方法第1次
执行方法第2次
Intercepotor:处理
执行方法第3次
Interceptor1: 处理
myController
Interceptor1: 处理modleandview
Interceptor: 处理modleandview
1释放资源
释放资源