静态代码块在Spring容器启动时必然失败,因其在JVM类加载阶段执行,早于ApplicationContext创建和Bean初始化,此时调用SpringUtil.getBean()或依赖注入字段均返回null。

静态代码块在 Spring 容器启动时无法安全访问 Bean,因为它执行于类加载阶段(JVM 控制),远早于 Spring 容器初始化。此时调用 SpringUtil.getBean() 或依赖注入字段,必然返回 null,引发 NullPointerException。这不是配置错误,而是 JVM 与 Spring 生命周期的根本错位。
静态代码块为何必然失败
Java 类加载的“准备→初始化”阶段会立即执行 static { ... } 块,而此时:
- Spring 的
ApplicationContext还未创建,更无 Bean 实例 - @Autowired、@Value 等注解尚未解析,对静态成员完全无效
- 任何通过工具类(如
SpringUtil)提前获取 Bean 的尝试都会返回null - 哪怕该类本身是
@Component,其静态块仍独立于 Spring 生命周期运行
典型崩溃现场还原
以下代码看似合理,实则必崩:
❌ 危险示例(运行即 NPE)public class ReportGenerator {<br> static {<br> // Spring 容器此时为 null<br> TemplateEngine engine = SpringUtil.getBean(TemplateEngine.class); // 返回 null<br> engine.render("template"); // 抛出 NullPointerException<br> }<br>}
只要 JVM 加载 ReportGenerator(例如被其他类引用、被 ClassLoader 扫描到),该静态块就会触发,而 Spring 还在启动途中。
真正可行的替代方案
放弃静态代码块,改用 Spring 感知的生命周期钩子:
- @PostConstruct 方法:在 Bean 初始化完成后执行,确保容器就绪。适用于单例 Bean 内部的静态资源桥接
- 实现 ApplicationContextAware:在上下文可用后主动缓存所需 Bean 到静态字段,适合工具类
- 懒加载静态访问器:用双重检查锁 + volatile 静态字段,在首次调用时才从上下文取 Bean
-
启动后回调 ApplicationRunner:在
SpringApplication.run()完成后统一初始化所有静态依赖
推荐落地写法(@PostConstruct 桥接)
适用于已注册为 Bean 的工具类:
@Component<br>public class ConfigHolder {<br> private static AppConfig config;<br> @Autowired<br> private AppConfig appConfig;<br><br> @PostConstruct<br> public void init() {<br> config = appConfig; // 安全赋值,此时 appConfig 已注入<br> }<br><br> public static AppConfig getConfig() {<br> return config; // 外部可安全调用<br> }<br>}
该方式不破坏 Spring 管理逻辑,又满足静态访问需求,且线程安全、启动可靠。

















