Spring-Bean生命周期-用人的生命周期来理解

327 阅读4分钟

为了更清晰地理解Spring Bean的生命周期,我们用一个更贴近日常生活的比喻:人的一生。将Bean的生命周期与人类的成长阶段对应,结合代码示例逐步解析。


生命周期阶段与人类成长对照表

生命周期阶段人类成长阶段技术实现核心扩展点示例
1. 实例化婴儿出生new Bean()
2. 属性注入学习语言/知识反射设置字段值@Autowired
3. 初始化前增强成年礼前的准备BeanPostProcessor.postProcessBeforeInitialization@PostConstruct
4. 自定义初始化成年仪式InitializingBean.afterPropertiesSet()自定义init-method
5. 初始化后增强大学毕业后的培训BeanPostProcessor.postProcessAfterInitializationAOP代理创建
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);

❓ 如何避免生命周期回调冲突?

  • 优先级规则
    1. @PostConstruct(JSR-250标准)
    2. InitializingBean接口
    3. 自定义init-method
  • 建议:一个Bean中只选择一种初始化方式

记忆强化练习

  1. 画时序图:用不同颜色区分Spring原生控制与开发者扩展点
  2. 断点调试:在以下位置设置断点观察调用栈:
    // 关键断点位置
    AbstractAutowireCapableBeanFactory.initializeBean()
    CommonAnnotationBeanPostProcessor.postProcessBeforeDestruction()
    
  3. 手写简化版IoC容器:实现最基本的生命周期管理(约200行代码)

通过将抽象概念具象化为人类成长过程,结合可验证的代码示例,能够帮助开发者建立直觉性理解。下次遇到Bean生命周期相关问题时,可以尝试在脑海中回放这个"人生剧本",就能快速定位各个阶段的执行位置。