导航
📖 本讲目标
- 理解 Bean 生命周期的完整流程
- 实现 @PostConstruct 初始化回调
- 实现 @PreDestroy 销毁回调
- 理解初始化方法的调用时机
🎯 Bean 生命周期概述
生命周期阶段
Bean 的完整生命周期包括以下阶段:
- 实例化(Instantiation):创建 Bean 实例
- 属性赋值(Population):注入依赖
- 初始化(Initialization):调用初始化方法
- 使用(In Use):Bean 可以被使用
- 销毁(Destruction):容器关闭时调用销毁方法
生命周期流程图
容器启动
↓
扫描并注册 BeanDefinition
↓
预实例化单例 Bean(非懒加载)
↓
┌─────────────────────────┐
│ 1. 实例化 Bean │ ← 调用构造函数
└─────────────────────────┘
↓
┌─────────────────────────┐
│ 2. 属性赋值 │ ← 依赖注入
└─────────────────────────┘
↓
┌─────────────────────────┐
│ 3. 初始化 │ ← @PostConstruct / initMethod
└─────────────────────────┘
↓
┌─────────────────────────┐
│ 4. Bean 可用 │
└─────────────────────────┘
↓
容器关闭
↓
┌─────────────────────────┐
│ 5. 销毁 │ ← @PreDestroy / destroyMethod
└─────────────────────────┘
🏷️ @PostConstruct 注解实现
注解定义
创建 src/main/java/com/minispring/beans/factory/annotation/PostConstruct.java:
package com.minispring.beans.factory.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @PostConstruct 注解用于标记初始化方法
* 在 Bean 创建完成、依赖注入完成后执行
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PostConstruct {
}
使用示例
@Component
public class UserRepository {
@PostConstruct
public void init() {
System.out.println("UserRepository 初始化完成");
// 初始化逻辑...
}
}
🏷️ @PreDestroy 注解实现
注解定义
创建 src/main/java/com/minispring/beans/factory/annotation/PreDestroy.java:
package com.minispring.beans.factory.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @PreDestroy 注解用于标记销毁方法
* 在容器关闭时,销毁单例 Bean 之前执行
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreDestroy {
}
使用示例
@Component
public class DatabaseConfig {
@PreDestroy
public void cleanup() {
System.out.println("DatabaseConfig 清理资源");
// 清理逻辑...
}
}
🔧 初始化方法实现
实现思路
初始化方法的调用时机:
- Bean 实例化完成
- 依赖注入完成
- 在 Bean 可用之前
实现代码
在 ApplicationContext 中添加初始化方法:
/**
* 初始化 Bean(调用初始化方法)
* @param bean Bean 实例
* @param beanName Bean 名称
* @param bd Bean 定义
*/
private void initializeBean(Object bean, String beanName, BeanDefinition bd) throws Exception {
Class<?> clazz = bd.getBeanClass();
// 1. 查找 @PostConstruct 注解的方法
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
method.setAccessible(true);
method.invoke(bean);
System.out.println(" ✓ 调用 @PostConstruct 方法: " + method.getName());
}
}
// 2. 调用指定的初始化方法(通过 BeanDefinition 配置)
if (bd.getInitMethodName() != null && !bd.getInitMethodName().isEmpty()) {
Method initMethod = clazz.getMethod(bd.getInitMethodName());
initMethod.setAccessible(true);
initMethod.invoke(bean);
System.out.println(" ✓ 调用初始化方法: " + bd.getInitMethodName());
}
}
更新 createBean 方法
private Object createBean(String beanName, BeanDefinition bd) throws Exception {
// 1. 检查循环依赖
if (singletonsCurrentlyInCreation.contains(beanName)) {
throw new RuntimeException("检测到循环依赖: " + beanName);
}
// 2. 标记为正在创建
singletonsCurrentlyInCreation.add(beanName);
try {
System.out.println("创建 Bean: " + beanName +
" (" + bd.getBeanClass().getSimpleName() + ")");
// 3. 实例化 Bean(支持构造函数注入)
Object bean = instantiateBean(beanName, bd);
// 4. 字段注入
populateBean(bean);
// 5. 初始化回调
initializeBean(bean, beanName, bd);
return bean;
} finally {
// 6. 移除创建中标记
singletonsCurrentlyInCreation.remove(beanName);
}
}
🔧 销毁方法实现
实现思路
销毁方法的调用时机:
- 容器关闭时(调用
close()方法) - 只销毁单例 Bean
- 按照注册顺序的逆序销毁
实现代码
在 ApplicationContext 中添加销毁方法:
/**
* 关闭容器,清理资源
*/
public void close() {
System.out.println("n容器关闭中...");
// 调用所有单例 Bean 的销毁方法
destroySingletons();
// 清空缓存
singletonObjects.clear();
beanDefinitionMap.clear();
singletonsCurrentlyInCreation.clear();
System.out.println("容器已关闭");
}
/**
* 销毁所有单例 Bean(调用销毁方法)
*/
private void destroySingletons() {
for (Map.Entry<String, Object> entry : singletonObjects.entrySet()) {
String beanName = entry.getKey();
Object bean = entry.getValue();
BeanDefinition bd = beanDefinitionMap.get(beanName);
if (bd == null) {
continue;
}
Class<?> clazz = bd.getBeanClass();
// 1. 查找 @PreDestroy 注解的方法
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(PreDestroy.class)) {
try {
method.setAccessible(true);
method.invoke(bean);
System.out.println(" ✓ 调用 @PreDestroy 方法: " + beanName + "." + method.getName());
} catch (Exception e) {
System.err.println(" ✗ 调用 @PreDestroy 方法失败: " + beanName + "." + method.getName() +
" - " + e.getMessage());
}
}
}
// 2. 调用指定的销毁方法(通过 BeanDefinition 配置)
if (bd.getDestroyMethodName() != null && !bd.getDestroyMethodName().isEmpty()) {
try {
Method destroyMethod = clazz.getMethod(bd.getDestroyMethodName());
destroyMethod.setAccessible(true);
destroyMethod.invoke(bean);
System.out.println(" ✓ 调用销毁方法: " + beanName + "." + bd.getDestroyMethodName());
} catch (Exception e) {
System.err.println(" ✗ 调用销毁方法失败: " + beanName + "." + bd.getDestroyMethodName() +
" - " + e.getMessage());
}
}
}
}
📋 初始化方法调用顺序
调用顺序
- @PostConstruct 方法:优先调用所有带 @PostConstruct 注解的方法
- initMethod:然后调用 BeanDefinition 中配置的初始化方法
代码示例
@Component
public class UserService {
@PostConstruct
public void postConstruct1() {
System.out.println("PostConstruct 1");
}
@PostConstruct
public void postConstruct2() {
System.out.println("PostConstruct 2");
}
public void init() {
System.out.println("Init Method");
}
}
调用顺序:
- postConstruct1()
- postConstruct2()
- init()(如果配置了 initMethodName)
📋 销毁方法调用顺序
调用顺序
- @PreDestroy 方法:优先调用所有带 @PreDestroy 注解的方法
- destroyMethod:然后调用 BeanDefinition 中配置的销毁方法
代码示例
@Component
public class DatabaseConfig {
@PreDestroy
public void preDestroy1() {
System.out.println("PreDestroy 1");
}
@PreDestroy
public void preDestroy2() {
System.out.println("PreDestroy 2");
}
public void cleanup() {
System.out.println("Cleanup Method");
}
}
调用顺序:
- preDestroy1()
- preDestroy2()
- cleanup()(如果配置了 destroyMethodName)
🧪 测试示例
测试类
package com.minispring.demo;
import com.minispring.beans.factory.annotation.PostConstruct;
import com.minispring.beans.factory.annotation.PreDestroy;
import com.minispring.stereotype.Component;
import com.minispring.beans.factory.config.BeanDefinition;
import com.minispring.context.ApplicationContext;
@Component
class LifecycleBean {
@PostConstruct
public void init() {
System.out.println(" [LifecycleBean] @PostConstruct 方法被调用");
}
@PreDestroy
public void destroy() {
System.out.println(" [LifecycleBean] @PreDestroy 方法被调用");
}
public void doSomething() {
System.out.println(" [LifecycleBean] 执行业务逻辑");
}
}
public class LifecycleTest {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ApplicationContext();
// 注册 Bean
BeanDefinition bd = new BeanDefinition(LifecycleBean.class);
context.registerBeanDefinition("lifecycleBean", bd);
// 获取 Bean(会触发初始化)
LifecycleBean bean = context.getBean("lifecycleBean", LifecycleBean.class);
bean.doSomething();
// 关闭容器(会触发销毁)
context.close();
}
}
📝 本讲总结
本讲我们完成了:
- ✅ 理解了 Bean 生命周期的完整流程
- ✅ 实现了 @PostConstruct 初始化回调
- ✅ 实现了 @PreDestroy 销毁回调
- ✅ 实现了初始化方法的调用机制
- ✅ 实现了销毁方法的调用机制
- ✅ 理解了初始化/销毁方法的调用顺序
🎯 关键点回顾
- 生命周期阶段:实例化 → 属性赋值 → 初始化 → 使用 → 销毁
- @PostConstruct:在依赖注入完成后调用
- @PreDestroy:在容器关闭时调用
- 调用顺序:注解方法优先,然后是指定方法
⚠️ 注意事项
- 初始化方法:可以有多个 @PostConstruct 方法
- 销毁方法:可以有多个 @PreDestroy 方法
- 异常处理:销毁方法中的异常不应影响其他 Bean 的销毁
- Prototype Bean:容器不管理 Prototype Bean 的销毁
🚀 下一讲预告
在下一讲中,我们将实现 组件扫描机制,包括:
- @Component 注解
- 类路径扫描实现
- 自动注册 BeanDefinition
- 包扫描优化
准备好了吗?让我们继续 第 08 讲:组件扫描机制!
思考题:
- 为什么初始化方法在依赖注入之后调用?
- 销毁方法为什么只对单例 Bean 有效?
- 如何保证初始化方法的执行顺序?
