@Configuration类中ApplicationContext未注入,myBean初始化触发空指针异常求助
我之前碰到过好几个小伙伴踩这个坑,其实核心原因不是ApplicationContext没被注入,而是你的myBean被Spring提前实例化了,刚好赶在配置类的ApplicationContext字段赋值之前。其他配置类正常,大概率是因为它们的Bean初始化顺序更晚,或者用了更可靠的注入方式。下面给你几个靠谱的解决方案,按优先级排序:
1. 最推荐:直接在@Bean方法参数中注入ApplicationContext
这是Spring官方最推荐的方式,完全规避初始化顺序问题——Spring会自动确保调用myBean()方法时,ApplicationContext已经准备就绪:
@Configuration public class MyProblematicConfig { @Bean public MyBean myBean(ApplicationContext context) { // 这里的context绝对不会为null return new MyBean(context); } }
为什么这个管用?因为Spring处理@Bean方法时,会自动解析方法参数,把对应的依赖(包括ApplicationContext这种核心容器对象)提前准备好再调用方法,根本不会出现依赖未就绪的情况。
2. 用ApplicationContextAware接口替代@Autowired字段注入
如果一定要在配置类中持有ApplicationContext实例,别用@Autowired字段,实现ApplicationContextAware接口是更可靠的方式——Spring会在配置类实例化后立即回调setApplicationContext方法,远早于@Bean方法的执行:
@Configuration public class MyProblematicConfig implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 这个方法会在配置类刚创建就执行 this.context = applicationContext; } @Bean public MyBean myBean() { // 这里context肯定已经被赋值了 return new MyBean(context); } }
对比@Autowired字段:@Autowired的字段注入是在配置类实例化后的postProcessProperties阶段执行的,比ApplicationContextAware的回调晚,如果你的myBean因为某些原因被提前触发初始化,就会碰到null的情况。
3. 排查并调整Bean的初始化顺序
如果上面两种方法都不想用,那得找到为什么myBean会被提前初始化:
- 检查是否有其他Bean直接依赖
myBean,并且那个Bean的初始化优先级更高(比如用了@Priority、@Order,或者是Spring内部的基础设施Bean) - 检查
myBean是否被@ConditionalOnBean、@ConditionalOnMissingBean这类注解触发了提前初始化 - 如果是同一个配置类里的其他@Bean方法调用了
myBean(),记得Spring会代理配置类,直接调用方法会返回代理后的Bean,但如果是跨配置类调用,就会触发提前实例化
找到原因后,可以用@DependsOn注解强制指定myBean的依赖顺序,比如让它依赖一个肯定在ApplicationContext之后初始化的Bean(不过一般不推荐,因为会增加耦合):
@Bean @DependsOn("someBeanThatDependsOnApplicationContext") public MyBean myBean() { return new MyBean(context); }
最后提醒一句:尽量避免在配置类中直接持有ApplicationContext实例,除非真的必要。用方法参数注入或者让Bean自己实现ApplicationContextAware(而不是配置类),是更符合Spring设计理念的做法。
内容的提问来源于stack exchange,提问作者Monkey Supersonic

