Spring Boot 2.3.8升级2.5.6后ApplicationContext为空报错问题
问题根因
- 首先你的
SpringApplicationContextProvider实现存在不规范写法:静态字段上的@Autowired本身就是Spring官方不支持的注入方式,你之前能正常运行完全是依赖ApplicationContextAware接口的setApplicationContext方法对静态字段赋值,加的@Autowired完全无效。 - 本次版本升级触发了时序问题:Spring Boot 2.4+调整了嵌入式Tomcat的启动时序,
ServletContextListener的contextInitialized方法触发时机比老版本更早,从异常栈可以看到,这个触发时机处于Spring上下文刷新的中间阶段,还没来得及执行ApplicationContextAware的setter方法,所以静态的applicationContext还是null,就抛出空指针。 - 额外排查点:如果开启了
spring.main.lazy-initialization=true,或者你的SpringApplicationContextProvider所在包没有被组件扫描覆盖,也会导致该Bean没有被初始化,无法触发setter赋值。
排查步骤
- 在
setApplicationContext方法中增加日志打印,确认该方法的执行时间是否晚于你调用getBeanFactory的时间,确认是时序问题。 - 检查启动类的
@ComponentScan配置,确认SpringApplicationContextProvider所在的包在扫描路径内。 - 检查全局配置是否开启了延迟初始化开关。
解决方案
最优方案(推荐)
替换掉ServletContextListener里的启动逻辑,改用Spring原生的生命周期回调执行初始化操作,ApplicationRunner/CommandLineRunner或者ContextRefreshedEvent事件监听都会在Spring上下文完全刷新、所有Bean初始化完成后触发,完全规避空指针问题。
示例代码:
@Component public class AppStartupRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { // 将原Startup.contextInitialized中的初始化逻辑迁移到此处 ApplicationCache.getInstance(); } }
兼容方案(不改原有监听器逻辑)
手动调高SpringApplicationContextProvider的初始化优先级,保证它在监听器触发前完成赋值,首先删掉代码中静态字段上无效的@Autowired注解,再给类加上最高优先级的Order注解:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class SpringApplicationContextProvider implements ApplicationContextAware { // 删掉@Autowired private static ApplicationContext applicationContext; // 其余代码保持不变 }
如果上述配置还是不生效,可以在启动类中手动注册该Bean,强制提前赋值:
@SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication app = new SpringApplication(Main.class); app.addInitializers(context -> { SpringApplicationContextProvider provider = new SpringApplicationContextProvider(); provider.setApplicationContext(context); context.getBeanFactory().registerSingleton("springApplicationContextProvider", provider); }); app.run(args); } }
内容的提问来源于stack exchange,提问作者pratik
相关产品推荐
相关产品推荐

