2025年11月23日

[手写 MiniSpring 框架教程] 第 07 讲:Bean 生命周期管理

📖 本讲目标

  • 理解 Bean 生命周期的完整流程
  • 实现 @PostConstruct 初始化回调
  • 实现 @PreDestroy 销毁回调
  • 理解初始化方法的调用时机

🎯 Bean 生命周期概述

生命周期阶段

Bean 的完整生命周期包括以下阶段:

  1. 实例化(Instantiation):创建 Bean 实例
  2. 属性赋值(Population):注入依赖
  3. 初始化(Initialization):调用初始化方法
  4. 使用(In Use):Bean 可以被使用
  5. 销毁(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 清理资源");
        // 清理逻辑...
    }
}

🔧 初始化方法实现

实现思路

初始化方法的调用时机:

  1. Bean 实例化完成
  2. 依赖注入完成
  3. 在 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);
    }
}

🔧 销毁方法实现

实现思路

销毁方法的调用时机:

  1. 容器关闭时(调用 close() 方法)
  2. 只销毁单例 Bean
  3. 按照注册顺序的逆序销毁

实现代码

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());
            }
        }
    }
}

📋 初始化方法调用顺序

调用顺序

  1. @PostConstruct 方法:优先调用所有带 @PostConstruct 注解的方法
  2. 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");
    }
}

调用顺序

  1. postConstruct1()
  2. postConstruct2()
  3. init()(如果配置了 initMethodName)

📋 销毁方法调用顺序

调用顺序

  1. @PreDestroy 方法:优先调用所有带 @PreDestroy 注解的方法
  2. 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");
    }
}

调用顺序

  1. preDestroy1()
  2. preDestroy2()
  3. 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();
    }
}

📝 本讲总结

本讲我们完成了:

  1. ✅ 理解了 Bean 生命周期的完整流程
  2. ✅ 实现了 @PostConstruct 初始化回调
  3. ✅ 实现了 @PreDestroy 销毁回调
  4. ✅ 实现了初始化方法的调用机制
  5. ✅ 实现了销毁方法的调用机制
  6. ✅ 理解了初始化/销毁方法的调用顺序

🎯 关键点回顾

  1. 生命周期阶段:实例化 → 属性赋值 → 初始化 → 使用 → 销毁
  2. @PostConstruct:在依赖注入完成后调用
  3. @PreDestroy:在容器关闭时调用
  4. 调用顺序:注解方法优先,然后是指定方法

⚠️ 注意事项

  1. 初始化方法:可以有多个 @PostConstruct 方法
  2. 销毁方法:可以有多个 @PreDestroy 方法
  3. 异常处理:销毁方法中的异常不应影响其他 Bean 的销毁
  4. Prototype Bean:容器不管理 Prototype Bean 的销毁

🚀 下一讲预告

在下一讲中,我们将实现 组件扫描机制,包括:

  • @Component 注解
  • 类路径扫描实现
  • 自动注册 BeanDefinition
  • 包扫描优化

准备好了吗?让我们继续 第 08 讲:组件扫描机制


思考题

  1. 为什么初始化方法在依赖注入之后调用?
  2. 销毁方法为什么只对单例 Bean 有效?
  3. 如何保证初始化方法的执行顺序?

“以书为舟,遨游尘世”,
最好的免费 kindle 电子书分享站:

You may also like...

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注


*