Spring Boot中@Autowired注入依赖的内部成员为null问题咨询
问题根本原因
- 你的代码中存在两类
BeanTest实例:- 第一类是你在
TimeOutConfig的@Bean方法中手动new BeanTest(value1, value2)生成的实例,该实例调用带参构造,执行了beanWebClient()方法,内部b1属性有值,你通过构造方法将该实例传给了手动创建的User对象。 - 第二类是Spring扫描到
BeanTest类上的@Component注解后,自动调用无参构造生成的全局单例BeanTest实例,该实例的v1、v2为默认值0,也没有执行beanWebClient()方法,内部b1属性为null。
- 第一类是你在
- 当你在
User类的beanTest字段上加了@Autowired注解后,Spring会对所有纳入容器管理的User实例(包括你在配置类中通过@Bean注册的两个User实例)执行字段注入逻辑:Spring会将自己管理的全局单例、无参构造生成的BeanTest实例注入到beanTest字段,直接覆盖了你之前通过构造方法手动赋值的带参数BeanTest实例。 - 此时
User中的beanTest实际是Spring生成的无参BeanTest,内部b1为null,调用message()方法访问b1时就会抛出空指针异常。 - 当你去掉
@Autowired注解后,Spring不会对beanTest字段执行注入覆盖,保留了你手动赋值的带参数BeanTest实例,因此运行正常。
正确实现方案
你的需求是每个User实例绑定对应自定义参数的BeanTest,不需要依赖Spring全局注入BeanTest,可选两种优化方案:
方案1:移除User的@Component注解,完全通过@Bean注册实例
你已经在配置类中手动注册了两个User的@Bean,不需要再给User类加@Component注解,额外加的@Component会生成无用的全局User单例,还容易引发注入混淆。此时可以保留字段不加@Autowired,完全通过构造方法赋值即可,也就是你当前正常运行的写法:
// 移除@Component注解 public class User { private final BeanTest beanTest; public User(BeanTest beanTest){ this.beanTest = beanTest; } public void message() { beanTest.message(); } }
方案2:将自定义的BeanTest也注册为Spring Bean
如果你需要让Spring自动注入BeanTest,可以把每个自定义参数的BeanTest也注册为Spring容器中的Bean,再和对应的User绑定:
@Configuration public class TimeOutConfig { @Value("${value1}") private int value1; @Value("${value2}") private int value2; @Value("${value3}") private int value3; @Value("${value4}") private int value4; @Bean BeanTest boBeanTest() { return new BeanTest(value1, value2); } @Bean User boTimeOut(@Qualifier("boBeanTest") BeanTest boBeanTest) { System.out.println("In BOTimeOutConfig"); return new User(boBeanTest); } @Bean BeanTest bseBeanTest() { return new BeanTest(value3, value4); } @Bean(name = "BSETimeout") User getBSEUser(@Qualifier("bseBeanTest") BeanTest bseBeanTest) { System.out.println("In BSETimeOutConfig"); return new User(bseBeanTest); } }
这种场景下也推荐使用构造方法注入代替字段注入,Spring 4.3+之后如果类只有一个构造方法,会自动注入构造方法的参数,不需要额外加@Autowired注解。
额外优化建议
- 不要同时给类加
@Component又在配置类中注册@Bean,会导致容器中存在多个同类型实例,容易引发注入混淆 - 手动
new出来的对象默认不归Spring管理,不会执行自动注入、AOP等逻辑,尽量把需要Spring管理的对象都通过@Bean或者@Component注册到容器 - 优先使用构造方法注入代替字段注入,代码更健壮,方便单元测试,也不会出现注入覆盖的问题
内容的提问来源于stack exchange,提问作者Aditya K
相关产品推荐
相关产品推荐

