Spring 中的拦截器与过滤器

在 SpringBoot 的 Web 项目开发中,如果想实现拦截、过滤的功能,大概会有三种做法:Filter 过滤器、Interceptor 拦截器、AOP 切面编程,而我们今天要讨论的是 Filter 与 Interceptor 的做法及它们之间的区别。

Filter 过滤器

Filter 是 Servlet 中用于拦截请求、过滤请求的一个接口。在以前,我们通常会使用 Filter 来拦截请求设置请求的字符集、判断用户是否登陆、校验权限等等。

其工作原理和核心配置文件 web.xml 息息相关,在配置文件中我们会配置过滤器的名称,以及它过滤的 URL 规则。配置好后,符合过滤规则的请求就会先来到过滤器这里执行 Filter 中的逻辑,以及判断是否能进行下一步的流转。

虽然使用原生的 Servlet 开发的时代大概率已经过去,但是 Servlet 却是 Web 开发基础中的基础,所以 Filter 接口也是能适用于 SpringBoot 项目的。

Filter 方法简单介绍

public interface Filter {

  	//Servlet容器(如Tomcat)在初始化这个Filter时调用,一般用于初始化一些资源
    public default void init(FilterConfig filterConfig) throws ServletException {}

  	//这个方法是具体执行过滤器逻辑的方法
  	//另外chain变量是过滤器链,可以使用这个变量来决定这个请求是否可以向下流转
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

  	//Servlet容器(如Tomcat)在关闭前会销毁Filter,一般用于资源的释放
    public default void destroy() {}
}

SpringBoot 中添加 Filter

SpringBoot 项目中添加 Filter 的步骤主要包括 Filter 定义与注册,添加的方式有 3 种,下面一一做展示

  • 方式一:使用 @WebFilter 注解 + @ServletComponentScan 注解

    // 过滤器
    package com.example.demo.filter.one;
    
    @WebFilter(filterName = "filter-one", urlPatterns = "/bad/*")
    public class FilterOne implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //执行Filter逻辑
            System.out.println("这是Filter过滤器1号");
            //让请求继续进入Filter链的下一个节点
            chain.doFilter(request, response);
        }
    }
    
    
    // 启动类
    @SpringBootApplication
    @ServletComponentScan(basePackages = "com.example.demo.filter.one")
    public class SpringBootDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootDemoApplication.class, args);
        }
    
    }
    
  • 方式二:使用 FilterRegistrationBean 来注册一般过滤器

    // 过滤器
    public class FilterTwo implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //执行Filter逻辑
            System.out.println("这是Filter过滤器2号");
            //让请求继续进入Filter链的下一个节点
            chain.doFilter(request, response);
        }
    }
    
    
    // 配置类
    @Configuration
    public class FilterConfiguration {
        @Bean
        public FilterRegistrationBean<FilterTwo> filterRegistrationBean() {
            FilterTwo filterTwo = new FilterTwo();
            FilterRegistrationBean<FilterTwo> filterRegistrationBean = new FilterRegistrationBean<>();
            filterRegistrationBean.setFilter(filterTwo);
            //设置过滤器名、过滤规则
            filterRegistrationBean.setName("filter-two");
            filterRegistrationBean.addUrlPatterns("/bad/*");
            return filterRegistrationBean;
        }
    }
    
  • 方式三:使用 DelegatingFilterProxyRegistrationBean 注册已被 Spring 管理的过滤器

    // 过滤器
    @Component
    public class FilterThree implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //执行Filter逻辑
            System.out.println("这是Filter过滤器3号");
            //让请求继续进入Filter链的下一个节点
            chain.doFilter(request, response);
        }
    }
    
    
    // 配置类
    @Configuration
    public class FilterConfiguration {
        @Bean
        public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean() {
            DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean = new DelegatingFilterProxyRegistrationBean("filterThree");
            delegatingFilterProxyRegistrationBean.setName("filter-three");
            delegatingFilterProxyRegistrationBean.addUrlPatterns("/bad/*");
            return delegatingFilterProxyRegistrationBean;
        }
    }
    

Filter 原理

简单介绍一下三种做法的 Filter 注册原理
  • 方式一,SpringBoot 在启动时,ServletComponentScanRegistrar 类实现了 ImportBeanDefinitionRegistrar 接口,负责把 @ServletComponentScan 中的包路径传递给 ServletComponentRegisteringPostProcessor 类,ServletComponentRegisteringPostProcessor 实现了 BeanFactoryPostProcessor 接口,在调用 postProcessBeanFactory() 方法时,使用 WebServletHandlerWebFilterHandlerWebListenerHandler 一个个对比,符合条件就调用 doHandle() 方法来把 Filter 作为 FilterRegistrationBean 类型的 Bean 注册到 Spring IoC 容器中。
  • 方式二和方式三差异不大
    • 相同点,无论是 FilterRegistrationBean 还是 DelegatingFilterProxyRegistrationBean,他们都是实现了 ServletContextInitializer 接口的,在调用 onStartup() 方法时,抽象基类 AbstractFilterRegistrationBean 会调用 addRegistration() 方法,这个方法就是根据两个子类中返回的 Filter ,添加到 Spring IoC 容器中。
    • 不同点,DelegatingFilterProxyRegistrationBean 通过传入的 targetBeanName 名字,在 Spring IoC 容器中查找该 Fillter 类型的 Bean,并通过 DelegatingFilterProxy 生成基于这个 Bean 的代理 Filter 对象;而 FilterRegistrationBean 则是直接设置一个 Filter ,因此这个 Filter 可以由 Spring IoC 容器管理,也可不用管理。如果一个 Filter 被声明为一个 Bean,而不通过 DelegatingFilterProxyRegistrationBean 添加到 Spring IoC 容器中,那么这个过滤器是无法添加过滤规则的,全局适用。
Filter 在请求中的工作流程

在一次请求里,Filter 不是独立工作,而是以 FilterChain 过滤链的形式来进行过滤,每次请求都根据 URL 的匹配规则来找到符合规则的 Filter ,组装成一条过滤链,请求经过过滤链后才能到达 DispatcherServlet

这个 ApplicationFilterChain 在整个过滤器的工作链路中是一个核心角色,在 createFilterChain() 方法中,会按顺序地添加符合规则的过滤器,组建成一条过滤器链交给 StandardWrapperValve,在调用过滤器逻辑时,直接拿这条过滤器链来做过滤,过滤器中维护了过滤器的顺序,接下来的逻辑就是各个 Filter 的过滤逻辑。执行完各个过滤器后,如果这个请求都通过了过滤,那么最终会来到 DispatcherServlet 中。

HandlerInterceptor 拦截器

拦截器是 Spring 中的内容,它依赖于 Spring 容器,能从 Spring 容器中获取其他 Bean;拦截器提供了更加细颗粒度的拦截功能,更能体现 AOP 思想。

HandlerInterceptor 方法简单介绍

public interface HandlerInterceptor {

  	//在请求被处理前(到达Controller前)进行处理,如果返回false,那么请求不往下进行
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

      return true;
    }
  
  	//在请求被处理后(执行完Controller逻辑后)进行处理
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable ModelAndView modelAndView) throws Exception {
    }
  
  	//在页面渲染结束后执行,一般用于资源释放
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        @Nullable Exception ex) throws Exception {
    }

}

SpringBoot 中添加 HandlerInterceptor

与 Filter 类似,添加 HandlerInterceptor 的步骤也分为两步,定义与注册

// 拦截器
@Component
public class HandlerInterceptorOne implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("请求到Controller前-执行 HandlerInterceptor 逻辑");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Controller执行完后-执行 HandlerInterceptor 逻辑");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("返回视图前-执行 HandlerInterceptor 逻辑");
    }
}


// 配置类
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {
    @Autowired
    private HandlerInterceptorOne handlerInterceptorOne;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //这册拦截器
        registry.addInterceptor(handlerInterceptorOne)
                //设置拦截的路径
                .addPathPatterns("/bad/*")
                //设置不拦截的路径(排除这些路径)
                .excludePathPatterns("/bad/test");
    }
}

HandlerInterceptor 原理

HandlerInterceptor 的工作与 Filter 差别不大,先往容器里注册拦截器,当请求来到 DispatcherServlet 时,调用 getHandler() 方法根据请求的 URL 从容器中取出 URL 符合拦截规则的拦截器,组装成一条拦截器链 HandlerExecutionChain 。然后 DispatcherServlet 按照 preHandle -> handle(Controller) -> postHandle -> afterCompletion 的顺序往下执行。

总结

虽然两者名字上、功能上都颇为相似,但他们还是有部分区别的:

  • 从执行顺序上看:Filter 是 Servlet 容器接收到请求后,但是在调用 Servlet 被调用执行前执行的;而 Interceptor 是 Servlet 被调用后,在请求到达 Controller 前执行的
  • 从拦截粒度来看:Filter 只能对 request、response 进行拦截;Interceptor 不仅可以对 request、response 进行操作,也可以对 handler、modelAndView 进行操作,具备了对 SpringMVC 组件的操作能力
  • 从依赖从属来看:Filter 依赖于 Servlet 容器;而 Interceptor 不依赖于 Servlet,依赖于 Spring 框架

综上所述,在基于 SpringBoot 的项目开发中,如果有需要对请求拦截处理的场景,Filter 和 HandlerInterceptor两者之间,优先选择 HandlerInterceptor

参考资料

springboot 中 HandlerInterceptor和Filter区别及使用

关于springboot中添加Filter的方法

【Springboot】拦截器

Spring MVC HandlerInterceptor 实现原理(源码)

感谢你看到最后,如果你觉得这篇文章对你有帮助,请给我一个免费的大拇哥👍~

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot拦截器过滤器有一些区别。拦截器Spring MVC自带的,不依赖于servlet容器,而过滤器依赖于servlet容器。拦截器是基于Java的反射机制,而过滤器是基于函数的回调。拦截器只能对action请求起作用,而过滤器可以对几乎所有的请求起作用。拦截器可以获取IOC容器的bean,而过滤器不可以。拦截器是由Spring MVC提供的,可以在Controller访问服务层。而过滤器JavaEE标准,只需依赖servlet API,不需要依赖Spring。在Spring Boot配置拦截器可以使用@WebFilter注解,并在启动类加上@ServletComponentScan注解指定扫描的包。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [Springboot--拦截器过滤器 区别,作用,实现方法](https://blog.csdn.net/Dark_AK44/article/details/123746613)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [springboot过滤器拦截器](https://blog.csdn.net/qq_42076204/article/details/125215984)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值