解决SpringBoot中Filter中@Autowired注解的类成员变量为NULL

本文解决在SpringBoot中实现SSO单点登录时,自定义过滤器中注入的Bean为null的问题。通过调整Bean的初始化顺序,确保过滤器能够正确获取到所需的依赖。

1、问题描述

在采用SpringBoot中加入过滤器处理SSO单点登录过程中遇到所需要注入的类成员变量为Null的问题;

@Bean
public FilterRegistrationBean ssoCasRealmFilter() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new SsoCasRealmFilter());
    registration.addUrlPatterns("/*");
    registration.setName("SsoCasRealmFilter");
    registration.setOrder(Integer.MAX_VALUE-4);
    System.out.println("SsoCasRealmFilter");
    return registration;
}

public class SsoCasRealmFilter extends CommonSSOLoginFilter {
    @Autowired
    private PermissionService permissionService;//此处permissionService为NULL
    @Autowired
    private RedisService redisService;//此处redisService为NULL
    ....}

2、问题原因

在spring中,web的加载顺序是Listener->flter->servlet,所以说再在flter初始化时,相关的Bean还没有初始化,造成了对象为空。

3、解决问题

将该自定义Filter能作为Spring Bean,以便获得Spring Bean的相关益处,比如可以使用@Autowired进行Bean的注入等。但这里遇到了一个问题,自定义Filter通过@Bean注解后,被Spring Boot自动注册到了容器的Filter chain中,这样导致的结果是,所有URL都会被自定义Filter过滤,而不是Shiro中配置的一部分URL

@Bean
public Filter ssoCasFilter(){
 	return new SsoCasRealmFilter();
}

@Bean
public FilterRegistrationBean ssoCasRealmFilter() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(ssoCasFilter());
    registration.addUrlPatterns("/*");
    registration.setName("SsoCasRealmFilter");
    registration.setOrder(Integer.MAX_VALUE-4);
    System.out.println("SsoCasRealmFilter");
    return registration;
}
<think>我们正在讨论SpringBootFilter拦截器无法通过@Value注解获取配置参数的问题。根据引用内容,我们知道这是因为Filter的初始化顺序早于Spring容器的初始化,导致依赖注入无法完成。下面我将详细解释原因并提供解决方案。###原因分析在SpringBoot应用中,组件的初始化顺序为:1.`Listener`2.`Filter`3.`Servlet``Filter`的初始化发生在Spring容器初始化之前。因此,当`Filter`初始化时:-Spring容器尚未完全初始化-依赖注入(包括`@Value`和`@Autowired`)无法执行-导致使用这些注解的字段为`null`###解决方案####方法1:通过`init`方法手动获取Bean(推荐)在`Filter`的`init`方法中,使用`WebApplicationContextUtils`获取Spring上下文,然后手动获取需要的Bean或配置值。**示例代码:**```java@ComponentpublicclassMyFilterimplementsFilter{privateStringconfigValue;//需要注入的配置值@Overridepublicvoidinit(FilterConfigfilterConfig){//获取Spring上下文ApplicationContextctx=WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());//从Spring环境中获取配置值Environmentenv=ctx.getEnvironment();configValue=env.getProperty("my.config.key");}@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{//使用configValueSystem.out.println("Configvalue:"+configValue);chain.doFilter(request,response);}}```####方法2:使用`@ConfigurationProperties`绑定到对象将配置参数绑定到一个配置类,然后在`Filter`中注入该配置类。注意:仍然需要在`init`方法中手动获取该配置类的Bean。**步骤:**1.创建配置类:```java@ConfigurationProperties(prefix="my")@Data//Lombok注解,自动生成getter/setterpublicclassMyConfig{privateStringconfigKey;}```2.在`Filter`中获取配置类:```java@ComponentpublicclassMyFilterimplementsFilter{privateMyConfigmyConfig;@Overridepublicvoidinit(FilterConfigfilterConfig){ApplicationContextctx=WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());myConfig=ctx.getBean(MyConfig.class);}@OverridepublicvoiddoFilter(...){Stringvalue=myConfig.getConfigKey();//...}}```####方法3:将Filter注册为SpringBean(需结合`FilterRegistrationBean`)通过`FilterRegistrationBean`注册`Filter`,并确保`Filter`本身是Spring管理的Bean,这样就能正常使用依赖注入。**配置类示例:**```java@ConfigurationpublicclassFilterConfig{@Value("${my.config.key}")privateStringconfigValue;@BeanpublicFilterRegistrationBean<MyFilter>myFilterRegistration(){FilterRegistrationBean<MyFilter>registration=newFilterRegistrationBean<>();registration.setFilter(newMyFilter(configValue));//通过构造器传入配置值registration.addUrlPatterns("/*");returnregistration;}}//Filter类(不需要@Component注解)publicclassMyFilterimplementsFilter{privatefinalStringconfigValue;publicMyFilter(StringconfigValue){this.configValue=configValue;}@OverridepublicvoiddoFilter(...){//使用configValue}}```###总结-**根本原因**:`Filter`初始化顺序早于Spring容器,导致依赖注入失效。-**解决方案**:1.在`init`方法中手动获取配置值或Bean(推荐)2.通过`FilterRegistrationBean`注册`Filter`并传递配置值§§相关问题§§1.如何在SpringBoot中自定义Filter的执行顺序?2.除了Filter,还有哪些Spring组件会受初始化顺序影响?3.SpringBoot中如何读取配置文件中的自定义属性?</think>### Spring Filter中@Value注解无法注入属性的原因及解决方案 #### **原因分析** 1. **初始化顺序问题** Spring Boot启动顺序为:`Listener` → `Filter` → `Servlet` → Spring容器初始化[^1][^2]。 Filter初始化时,Spring容器尚未完成依赖注入,导致`@Value`和`@Autowired`失效。 2. **生命周期隔离** Filter由**Servlet容器**(如Tomcat)管理,而非Spring IoC容器。因此: - Spring的依赖注入机制无法作用于Filter - `@Value`注解Filter中直接使用会返回`null` 3. **作用域不匹配** `@Value`依赖Spring的`Environment`环境,但Filter初始化时: ```mermaid graph LR A[Servlet容器启动] --> B[Filter初始化] B --> C[Spring容器初始化] C --> D[依赖注入完成] ``` #### **解决方案** ##### 方法1:通过`init()`手动注入(推荐) ```java @Component public class AuthFilter implements Filter { private String configValue; // 需注入的属性 @Override public void init(FilterConfig filterConfig) { // 获取Spring上下文 ApplicationContext ctx = WebApplicationContextUtils .getRequiredWebApplicationContext(filterConfig.getServletContext()); // 从环境变量读取配置 Environment env = ctx.getEnvironment(); configValue = env.getProperty("your.config.key"); // 替换为实际配置键 } @Override public void doFilter(...) { System.out.println("配置值: " + configValue); // 正常使用 chain.doFilter(request, response); } } ``` **优势**:完全遵循Filter生命周期,无时序问题。 ##### 方法2:通过`FilterRegistrationBean`注册 ```java @Configuration public class FilterConfig { @Value("${your.config.key}") // 此处可正常注入 private String configValue; @Bean public FilterRegistrationBean<AuthFilter> authFilter() { FilterRegistrationBean<AuthFilter> reg = new FilterRegistrationBean<>(); reg.setFilter(new AuthFilter(configValue)); // 通过构造器传值 reg.addUrlPatterns("/*"); return reg; } } // Filter类改造 public class AuthFilter implements Filter { private final String configValue; // final确保不可变 public AuthFilter(String configValue) { this.configValue = configValue; } } ``` **适用场景**:需精确控制Filter初始化参数时。 ##### 方法3:延迟加载(Lazy Init) ```java @Component public class AuthFilter implements Filter { @Autowired private ApplicationContext ctx; // 通过ApplicationContextAware注入 private String configValue; @Override public void doFilter(...) { if (configValue == null) { // 首次使用时加载 configValue = ctx.getEnvironment().getProperty("your.config.key"); } // 使用configValue... } } ``` **注意**:需配合`@Component`和`@ServletComponentScan`,但仍有线程安全风险。 #### **关键总结** | 方案 | 优点 | 缺点 | |---------------|-----------------------|---------------------| | `init()`注入 | 生命周期安全,无时序问题 | 需手动编码 | | 注册Bean传递 | 符合Spring设计规范 | 需额外配置类 | | 延迟加载 | 代码简洁 | 非线程安全,有性能损耗 | > **最佳实践**:优先选择**方法1**或**方法2**,避免在Filter中直接使用`@Value`注解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值