目录
1.2 依赖注入(Dependency Injection, DI)
1.3 BeanPostProcessor.postProcessBeforeInitialization
1.5 InitializingBean.afterPropertiesSet()
1.7 BeanPostProcessor.postProcessAfterInitialization
1. Bean初始化执行顺序
1.1 构造函数(Constructor)
-
首先调用 Bean 的构造函数创建实例。
-
此时依赖注入尚未完成,Bean 的属性还是默认值(如
null或基本类型的默认值)。
1.2 依赖注入(Dependency Injection, DI)
-
Spring 通过反射或 setter 方法注入依赖的属性(
@Autowired、@Resource、@Value等)。
1.3 BeanPostProcessor.postProcessBeforeInitialization
-
在所有初始化回调(
@PostConstruct、InitializingBean、initMethod)之前执行。 -
例如,
@PostConstruct的处理就是通过CommonAnnotationBeanPostProcessor在这个阶段触发的。
1.4 @PostConstruct 方法
-
由
CommonAnnotationBeanPostProcessor调用,在依赖注入完成后执行。 -
通常用于自定义初始化逻辑。
1.5 InitializingBean.afterPropertiesSet()
-
如果 Bean 实现了
InitializingBean接口,Spring 会调用afterPropertiesSet()方法。
1.6 自定义 initMethod
-
如果 Bean 在配置中指定了
initMethod(如@Bean(initMethod = "init")或 XML 配置的init-method),Spring 会调用它。
1.7 BeanPostProcessor.postProcessAfterInitialization
-
在所有初始化回调完成后执行。
-
例如,AOP 代理的创建通常发生在这个阶段。
1.8 完整执行顺序总结
-
构造函数(Bean 实例化)
-
依赖注入(属性赋值)
-
BeanPostProcessor.postProcessBeforeInitialization -
@PostConstruct方法 -
InitializingBean.afterPropertiesSet() -
自定义
initMethod -
BeanPostProcessor.postProcessAfterInitialization
public class ExampleBean implements InitializingBean, BeanPostProcessor {
public ExampleBean() {
System.out.println("1. 构造函数");
}
@Autowired
public void setDependency() {
System.out.println("2. 依赖注入");
}
@PostConstruct
public void postConstruct() {
System.out.println("3. @PostConstruct方法");
}
@Override
public void afterPropertiesSet() {
System.out.println("4. InitializingBean的afterPropertiesSet()");
}
public void init() {
System.out.println("5. 自定义init方法");
}
public void destroy() {
System.out.println("8. destroy方法");
}
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("6. postProcessBeforeInitialization方法");
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("7. postProcessAfterInitialization方法");
return o;
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public ExampleBean getExampleBean() {
return new ExampleBean();
}
}
输出:
1. 构造函数
2. 依赖注入
3. @PostConstruct方法
4. InitializingBean的afterPropertiesSet()
5. 自定义init方法
6. postProcessBeforeInitialization方法
7. postProcessAfterInitialization方法
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 { @Autowired private 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 { @Autowired private Dependency dependency; @PostConstruct public 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(显式赋值和初始化块先执行,构造器最后执行)。
2736

被折叠的 条评论
为什么被折叠?



