java构造块在字段初始化后、构造方法体前执行,按源码顺序运行;spring依赖注入则发生在构造完成之后,此时@autowired/@value字段仍为null。

Java 中构造块(即实例初始化块)的执行顺序与成员变量初始化、构造方法调用紧密相关,而“成员属性注入”通常指 Spring 等框架在对象创建后通过反射设置字段值(如 @Autowired 或 @Value)。这两者**不在同一阶段发生**:构造块属于 JVM 原生对象创建流程,而依赖注入是框架层的后置操作。
构造块与字段初始化的执行顺序
当 new 一个对象时,JVM 按如下顺序执行:
- 分配内存空间,将所有字段设为默认值(0、null、false)
- 执行父类字段初始化 → 父类构造块 → 父类构造方法
- 执行本类字段初始化(声明时赋值)→ 本类构造块 → 本类构造方法
注意:字段声明处的初始化(如 private String name = "default";)和构造块({ System.out.println("init block"); })都在构造方法体执行前完成,且构造块按代码出现顺序依次执行。
Spring 的成员属性注入发生在构造之后
Spring 默认使用无参构造器创建 Bean 实例,此时:
- 字段初始化和构造块已全部执行完毕
- 对象处于“已构造但未注入”状态,所有
@Autowired字段仍为 null(除非显式赋初值) - 随后 Spring 通过反射或 setter 方法注入依赖,此过程不触发任何构造块或字段初始化逻辑
例如:@Autowired private Service service; 在构造块里访问该字段,值一定是 null —— 因为注入尚未开始。
如何安全配合两者?
若需在对象初始化阶段使用注入的依赖,应避免在构造块或字段初始化中直接引用它们。可行方式包括:
- 使用
@PostConstruct方法:在依赖注入完成后自动调用,适合初始化逻辑 - 改用构造器注入:把依赖作为构造参数传入,确保在构造块执行时依赖已就位(构造块仍早于构造方法体,但依赖已在参数中可用)
- 延迟初始化:用
Objects.requireNonNull(service)在首次使用时校验,而非在构造块中提前访问
常见误区提醒
不要在构造块中调用含注入字段的业务方法;不要假设 @Value 字段在构造块中已生效;也不要依赖字段声明赋值去“覆盖”注入值——Spring 注入会覆盖声明时的默认值,但时机永远在构造流程之后。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











