You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot启动初始化的静态ApplicationContext变量后续访问为null问题排查

问题根因

你遇到的问题不是类卸载,本质是Spring Boot DevTools的双类加载器机制导致的静态变量隔离:
Spring Boot DevTools为了实现快速热重启,会启动两类类加载器:

  • 基础类加载器(AppClassLoader):加载不会频繁改动的第三方依赖类
  • 重启类加载器(RestartClassLoader):加载你编写的业务代码,每次代码变更后会销毁旧的重启类加载器,新建实例加载修改后的代码,实现快速重启
    你的场景中,Spring托管的ApplicationContextListener Bean是由RestartClassLoader加载的,setApplicationContext方法赋值的是RestartClassLoader加载的ApplicationContextListener类的静态变量。而你的SPI(META-INF/services)逻辑是由AppClassLoader加载的ApplicationContextListener类,两个类加载器加载的同一个全类名类是完全独立的Class对象,静态变量绑定到Class对象本身,所以你访问到的AppClassLoader加载的类的静态变量从来没有被赋值,自然为null。
疑问解答

1. 为什么应用上下文中的有效Bean对应类会被卸载?

类卸载的触发条件非常苛刻,需要同时满足:类的所有实例被GC回收、加载该类的ClassLoader被GC回收、没有任何地方引用该类的Class对象。你的ApplicationContextListener实例是Spring容器托管的Bean,只要Spring上下文没有销毁,实例就不会被回收,类也不可能被卸载,你之前的类卸载猜测不成立。

2. 如何修复该问题?

可按照优先级选择以下方案:

  • 方案1(最简便,无额外配置):移除spring-boot-devtools依赖。如果你不需要热重启功能,直接删除devtools依赖即可消除双类加载器机制,问题直接解决。
  • 方案2(保留DevTools热重启能力):配置DevTools将指定类交给基础类加载器加载,保证全应用只有一份ApplicationContextListener的Class对象,静态变量全局唯一。在application.properties中添加如下配置:
    # 替换为你自己的ApplicationContextListener的完整类路径
    spring.devtools.restart.additional-exclude=com/xxx/xxx/ApplicationContextListener.class
    
  • 方案3(适配SPI类加载逻辑):如果需要保留双类加载器机制,修改静态获取Bean的代码,主动用线程上下文类加载器(即RestartClassLoader)加载ApplicationContextListener类,再获取已赋值的静态变量:
    public static MyBean getMyBean() {
        try {
            // 替换为你自己的ApplicationContextListener的全类名
            Class<?> listenerClazz = Thread.currentThread().getContextClassLoader().loadClass("com.xxx.xxx.ApplicationContextListener");
            Field contextField = listenerClazz.getDeclaredField("CONTEXT");
            contextField.setAccessible(true);
            ApplicationContext context = (ApplicationContext) contextField.get(null);
            return context.getBean(MyBean.class);
        } catch (Exception e) {
            throw new RuntimeException("获取Bean实例失败", e);
        }
    }
    
  • 方案4(架构最优方案):避免用静态变量持有Spring上下文,将你的SPI实现类也注册为Spring Bean,直接通过依赖注入的方式注入需要的MyBean实例,从根源上规避类加载器和静态变量的问题。

内容的提问来源于stack exchange,提问作者Sergey Shcherbakov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 06:06:10