在spring boot工程中使用Filter时,@WebFilter 注解不生效的问题分析和解决方案

1. 问题描述

首先编写一个Filter类并通过@Component放入spring容器中,通过实现jakarta.servlet中提供的Filter接口完成过滤器的创建,代码如下。

java 复制代码
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter 前置.....");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("filter 后置.....");
    }
}

为了将过滤器配置到目标路径下,通常有两种方法:

使用servlet提供的@WebFilter注解完成过滤路径的配置。

java 复制代码
@Component
@WebFilter(urlPatterns = "/api/*",filterName = "myFilterName") 
public class MyFilter implements Filter {
  代码同上,省略
}

使用spring boot推荐的配置类FilterRegistrationBean完成过滤路径的配置。

笔者在使用@WebFilter注解进行过滤路径配置时发现,所有路径下的请求都被过滤了,不受@WebFilter注解中的参数控制,而使用spring推荐的配置类方法则不会出现这个问题。

2. 问题分析

@WebFilter、@WebServlet 和 @WebListener 等与Filter相关的注解均是在Servlet 容器中,spring容器中并没有这些注解,所以即使在Filter类中加上@WebFilter注解并配置好路径参数,spring工程中也读不到这个配置,spring会使用spring配置类的配置,如果spring配置类中没有对Filter进行配置,就会使用默认配置,即针对所以路径进行过滤,所以才会出现以上现象,使用@WebFilter配置类过滤路径,但Filter还是会对所有路径进行过滤。

3. 解决方案

3.1 spring配置类

最简单的解决方案就是不使用@WebFilter注解对过滤路径进行配置,而是使用spring推荐的配置类的方式进行配置。

java 复制代码
@Configuration  // 专门对 springMVC 底层做一些配置
public class MySpringMVCConfig implements WebMvcConfigurer{

    @Autowired
    private MyFilter myFilter;
    
    @Bean
    public FilterRegistrationBean getFilter1Registration() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(this.myFilter);
        //设置过滤器名称和路径,在过滤器类写了的话,这里不用重复写
        filterRegistrationBean.setName("filter");
        filterRegistrationBean.addUrlPatterns("/api/*");
        //设置过滤器执行顺序,数字越小,越早进行过滤,也可设置为负数
        filterRegistrationBean.setOrder(1);
        return filterRegistrationBean;
    }

}

3.2 @WebFilter注解

如果非要使用@WebFilter注解的话:

第一步:需要把@WebFilter添加到容器中,因为spring容器中并没有@WebFilter的注解,这个注解在servlet容器中,故需要在启动类中添加@ServletComponentScan 注解启用 Servlet 容器扫描 @WebFilter@WebServlet@WebListener 注解,将这些组件注册到 Servlet 容器中。

java 复制代码
@SpringBootApplication
@ServletComponentScan
public class SpringmvcRestfulCrudApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringmvcRestfulCrudApplication.class, args);
    }
}

增加此注解后,@WebFilter就成功进入到Serlet容器中,并生效了,能够根据读取@WebFilter中的配置对配置内的路径进行过滤。但如果访问的是@WebFilter配置内的路径,程序会对此路径进行两次过滤,如果访问的路径不在@WebFilter配置内,则依然会被过滤一次。

这是因为在Filter类中添加了@Component注解,如果同时使用@Component和@ServletComponentScan这两个注解,Filter 类既会被 Spring 容器实例化,也会被 Servlet 容器实例化,导致被实例化两次。spring容器中实例化的Filter依然使用配置类的FilterRegistrationBean配置,如果没有则使用默认配置进行所有路径过滤。

所以就会出现在启动类增加@ServletComponentScan注解后,如果路径在@WebFilter配置内的访问会被过滤两次,一次是servlet容器实例化的Filter过滤的,另一次是spring容器实例化的Filter过滤。如果路径不在@WebFilter配置内依然会被过滤一次,这一次是spring容器中的Filter使用了默认配置进行的全路径过滤。

第二步:为了使用@WebFilter进行配置,不受spring容器实例化的Filter干扰,即将Filter类中的@Component注解删掉,Filter类不受spring容器控制,也不会进行实例化,这样就不会有两个实例化的Filter进行相互干扰了。

java 复制代码
//@Component 不放到spring容器中管理
@WebFilter(urlPatterns = "/api/*",filterName = "myFilterName") 
public class MyFilter implements Filter {
  代码同上,省略
}

4 总结

综上所述,如果在spring boot工程中使用过滤器Filter,推荐在配置类中使用FilterRegistrationBean进行过滤路径配置,并在Filter类中添加@Component将Filter类加载到spring容器中。

如果要使用@WebFilter进行过滤路径的配置,则需要在启动类中添加@ServletComponentScan注解启用Servlet容器扫描@WebFilter注解,并删除Filter类中的@Component注解,防止spring实例化的Filter与Servlet容器实例化的Filter冲突。

今后读者在使用过程中只要意识到自己过滤器Filter是由spring实例化的还是servlet实例化的,两个实例化的Filter所使用的配置方式不一样,要使用对应的配置方式。万万不可同时使用@Component注解和@ServletComponetScan注解。

相关推荐
Estar.Lee4 分钟前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
弗拉唐1 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi771 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
2401_857610032 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
少说多做3432 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀2 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20202 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深2 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
凌冰_2 小时前
IDEA2023 SpringBoot整合MyBatis(三)
spring boot·后端·mybatis