为了更清晰地理解Spring Bean的生命周期,我们用一个更贴近日常生活的比喻:人的一生。将Bean的生命周期与人类的成长阶段对应,结合代码示例逐步解析。
生命周期阶段与人类成长对照表
生命周期阶段 | 人类成长阶段 | 技术实现核心 | 扩展点示例 |
---|---|---|---|
1. 实例化 | 婴儿出生 | new Bean() | 无 |
2. 属性注入 | 学习语言/知识 | 反射设置字段值 | @Autowired |
3. 初始化前增强 | 成年礼前的准备 | BeanPostProcessor.postProcessBeforeInitialization | @PostConstruct |
4. 自定义初始化 | 成年仪式 | InitializingBean.afterPropertiesSet() | 自定义init-method |
5. 初始化后增强 | 大学毕业后的培训 | BeanPostProcessor.postProcessAfterInitialization | AOP代理创建 |
6. 使用期 | 职业生涯 | 应用调用Bean的方法 | 无 |
7. 销毁前回调 | 退休前的交接 | @PreDestroy | 自定义destroy-method |
8. 销毁 | 生命结束 | DisposableBean.destroy() | 无 |
分阶段详解(附代码验证)
1️⃣ 实例化(婴儿出生)
比喻:母亲分娩,婴儿初次来到世界
代码验证:
public class Person {
public Person() {
System.out.println("=====> 1. 调用构造方法(实例化)");
}
}
技术本质:通过反射调用构造函数 clazz.getDeclaredConstructor().newInstance()
2️⃣ 属性注入(学习知识)
比喻:父母教孩子说话、认字
代码验证:
public class Person {
private String name;
@Autowired // 依赖注入
public void setName(String name) {
System.out.println("=====> 2. 属性注入:setName(" + name + ")");
this.name = name;
}
}
技术本质:通过反射调用setter方法或直接设置字段值
3️⃣ 初始化前增强(成年礼准备)
比喻:18岁成人礼前购买正装
代码验证:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if(bean instanceof Person) {
System.out.println("=====> 3. BeanPostProcessor.before初始化");
}
return bean;
}
}
public class Person {
@PostConstruct
public void initIDCard() {
System.out.println("=====> 3.1 @PostConstruct方法(办身份证)");
}
}
执行顺序:
BeanPostProcessor.before
→ @PostConstruct
4️⃣ 自定义初始化(成年仪式)
比喻:正式举办成人礼
代码验证:
public class Person implements InitializingBean {
@Override
public void afterPropertiesSet() {
System.out.println("=====> 4. InitializingBean.afterPropertiesSet()");
}
public void customInit() {
System.out.println("=====> 4.1 自定义init-method");
}
}
配置方式:
<bean id="person" class="com.example.Person" init-method="customInit"/>
5️⃣ 初始化后增强(职业培训)
比喻:大学毕业后参加岗前培训
代码验证:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if(bean instanceof Person) {
System.out.println("=====> 5. BeanPostProcessor.after初始化");
// 通常在这里创建AOP代理
}
return bean;
}
}
关键点:此处是AOP动态代理的创建时机
6️⃣ 使用期(职业生涯)
比喻:参加工作为社会创造价值
代码验证:
// 在业务代码中调用
Person person = context.getBean(Person.class);
person.work();
7️⃣ 销毁前回调(退休交接)
比喻:退休前交接工作资料
代码验证:
public class Person {
@PreDestroy
public void backupData() {
System.out.println("=====> 7. @PreDestroy方法(数据备份)");
}
}
8️⃣ 销毁(生命结束)
比喻:生命终结
代码验证:
public class Person implements DisposableBean {
@Override
public void destroy() {
System.out.println("=====> 8. DisposableBean.destroy()");
}
public void customDestroy() {
System.out.println("=====> 8.1 自定义destroy-method");
}
}
配置方式:
<bean id="person" class="com.example.Person" destroy-method="customDestroy"/>
完整执行顺序验证
运行以下测试代码:
public class LifecycleTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
context.close();
}
}
控制台输出:
=====> 1. 调用构造方法(实例化)
=====> 2. 属性注入:setName(张三)
=====> 3. BeanPostProcessor.before初始化
=====> 3.1 @PostConstruct方法(办身份证)
=====> 4. InitializingBean.afterPropertiesSet()
=====> 4.1 自定义init-method
=====> 5. BeanPostProcessor.after初始化
=====> 7. @PreDestroy方法(数据备份)
=====> 8. DisposableBean.destroy()
=====> 8.1 自定义destroy-method
常见问题深度解析
❓ 为什么要有多个初始化阶段?
- 设计哲学:Spring通过分层扩展点满足不同粒度的控制需求
@PostConstruct
:标注轻量级初始化(如缓存预热)InitializingBean
:处理强依赖的初始化(如数据库连接)BeanPostProcessor
:实现容器级的统一处理(如代理增强)
❓ 销毁阶段的执行条件是什么?
- 单例Bean:容器关闭时触发
- 原型Bean:Spring不会管理其销毁,需手动调用
context.destroyBean()
- 验证代码:
// 手动销毁原型Bean
Person protoPerson = context.getBean("protoPerson", Person.class);
context.getBeanFactory().destroyBean(protoPerson);
❓ 如何避免生命周期回调冲突?
- 优先级规则:
@PostConstruct
(JSR-250标准)InitializingBean
接口- 自定义
init-method
- 建议:一个Bean中只选择一种初始化方式
记忆强化练习
- 画时序图:用不同颜色区分Spring原生控制与开发者扩展点
- 断点调试:在以下位置设置断点观察调用栈:
// 关键断点位置 AbstractAutowireCapableBeanFactory.initializeBean() CommonAnnotationBeanPostProcessor.postProcessBeforeDestruction()
- 手写简化版IoC容器:实现最基本的生命周期管理(约200行代码)
通过将抽象概念具象化为人类成长过程,结合可验证的代码示例,能够帮助开发者建立直觉性理解。下次遇到Bean生命周期相关问题时,可以尝试在脑海中回放这个"人生剧本",就能快速定位各个阶段的执行位置。