Spring Bean初始化及@PostConstruc执行顺序
目录
1. Bean初始化执行顺序
2. 成员变量初始化顺序
2.1 普通Java类(非Spring环境)
(1) 默认初始化(即初始分配内存)
(2) 显式初始化
(3) 构造器初始化
(4)完整顺序
2.2 Spring管理的Bean(依赖注入场景)
(1) 普通成员变量
(2) 依赖注入的成员变量
(3) 生命周期回调的影响
(4)关键区别总结
2.3 final 成员变量
2.4 常见面试问题
1. Bean初始化执行顺序
-
构造函数 - 首先调用Bean的构造函数
-
依赖注入 - 完成所有依赖项的注入(@Autowired等)
-
@PostConstruct方法 - 执行带有
@PostConstruct
注解的方法 -
InitializingBean的afterPropertiesSet() - 如果Bean实现了
InitializingBean
接口 -
自定义init方法 - 执行通过
@Bean(initMethod="...")
或XML配置中init-method
指定的方法
@Component
public class ExampleBean implements InitializingBean {public ExampleBean() {System.out.println("1. 构造函数");}@Autowiredpublic void setDependency(SomeDependency dep) {System.out.println("2. 依赖注入");}@PostConstructpublic void postConstruct() {System.out.println("3. @PostConstruct方法");}@Overridepublic void afterPropertiesSet() {System.out.println("4. InitializingBean的afterPropertiesSet()");}@Bean(initMethod = "customInit")public void customInit() {System.out.println("5. 自定义init方法");}
}
2. 成员变量初始化顺序
在Java中,类的成员变量(非静态成员变量)的初始化时机取决于其定义方式(直接赋值、构造器赋值、初始化块等)以及是否涉及依赖注入(如Spring框架)。
2.1 普通Java类(非Spring环境)
成员变量的初始化顺序和时机如下:
(1) 默认初始化(即初始分配内存)
-
时机:对象实例化时(
new
关键字调用构造器之前)。 -
规则:所有成员变量先被赋予默认值(
int
→0
,boolean
→false
,引用类型→null
等)。
(2) 显式初始化
-
时机:紧随默认初始化之后,按代码中的声明顺序执行。
-
方式:
-
直接赋值:
-
public class MyClass {private int a = 10; // 显式赋值private String s = "hello"; // 显式赋值
}
- 初始化块(代码块):
public class MyClass {private int x;{x = 20; // 初始化块赋值}
}
(3) 构造器初始化
-
时机:在显式初始化之后,构造器最后执行。
-
特点:构造器中的赋值会覆盖之前的默认值或显式赋值。
public class MyClass {private int value;public MyClass() {this.value = 30; // 构造器赋值}
}
(4)完整顺序
默认值 → 显式赋值/初始化块 → 构造器赋值
2.2 Spring管理的Bean(依赖注入场景)
在Spring中,成员变量的初始化分为依赖注入和普通成员变量初始化两部分:
(1) 普通成员变量
-
初始化规则与普通Java类一致(默认值 → 显式赋值 → 构造器)。
-
示例:
@Component
public class MyBean {private int count = 100; // 显式赋值(Spring无关)public MyBean() {this.count = 200; // 构造器覆盖}
}
(2) 依赖注入的成员变量
-
时机:在Bean实例化后,由Spring容器通过反射或Setter方法注入。
-
字段注入(
@Autowired
):
在构造器和显式赋值之后,通过反射直接注入。@Component public class MyBean {@Autowiredprivate Dependency dependency; // Spring在对象构造后注入 }
-
构造器注入:
在实例化时通过构造器参数注入(等同于普通Java的构造器赋值)。@Component public class MyBean {private final Dependency dependency;public MyBean(Dependency dependency) {this.dependency = dependency; // 构造时注入} }
-
(3) 生命周期回调的影响
-
@PostConstruct
方法会在依赖注入完成后执行,此时所有成员变量(包括注入的依赖)已就绪:@Component public class MyBean {@Autowiredprivate Dependency dependency;@PostConstructpublic void init() {System.out.println(dependency); // 依赖已注入} }
(4)关键区别总结
场景 | 成员变量初始化时机 |
---|---|
普通Java类 | 默认值 → 显式赋值/初始化块 → 构造器赋值 |
Spring Bean(字段注入) | 默认值 → 显式赋值 → 构造器 → 依赖注入 → @PostConstruct |
Spring Bean(构造器注入) | 与普通Java类相同,依赖通过构造器参数传入 |
2.3 final
成员变量
-
必须在声明时、初始化块或构造器中赋值,否则编译错误。
public class MyClass {private final int x = 10; // 声明时赋值private final int y;{ y = 20; } // 初始化块赋值private final int z;public MyClass() {z = 30; // 构造器赋值}
}
2.4 常见面试问题
Q1: 为什么构造器里的赋值能覆盖显式赋值?
因为构造器是最后执行的,可以修改之前的值。
Q2: 以下代码的输出是什么?
public class Test {private int x = 10;{ x = 20; }public Test() {System.out.println(x);x = 30;}public static void main(String[] args) {new Test(); // 输出?}
}
--
答案:20(显式赋值和初始化块先执行,构造器最后执行)。