Bean生命周期 过程总结

Bean 的生命周期

Bean 的生命周期是指一个 Bean 从被 Spring 容器创建到最终被销毁的整个过程。理解这个过程,你就能知道在 Bean 的不同阶段可以做哪些事情(比如初始化资源、释放资源)。

Bean 的生命周期过程相对复杂,特别是初始化阶段,有很多可以插入自定义逻辑的地方。我们以一个单例 Bean 为例,它的生命周期大致如下:

  1. Bean 定义加载 (Loading the Bean Definition):

    • Spring 容器启动时,会读取你的配置信息(XML、注解、Java Config)。
    • 容器解析这些配置,生成 Bean 的定义信息(BeanDefinition),包括 Bean 的类名、作用域、依赖关系、属性值等。
  2. Bean 实例化 (Bean Instantiation):

    • 根据 Bean 定义,Spring 使用反射机制创建 Bean 的实例。这通常是通过调用 Bean 的构造器来完成的。
    • 此时,Bean 只是一个“裸”对象,它的属性还没有被填充,依赖也还没有被注入。
  3. 属性填充 (Property Population) / 依赖注入 (Dependency Injection):

    • Spring 根据 Bean 定义中的依赖关系和属性配置,将 Bean 依赖的其他 Bean 实例注入到当前 Bean 的对应属性中(通过 Setter 方法或直接设置字段)。
    • 如果使用了构造器注入,这一步在实例化时就完成了。
  4. 执行 BeanPostProcessor 的 postProcessBeforeInitialization 方法:

    • 在 Bean 完成属性填充后,但在任何初始化方法被调用之前,Spring 会调用所有实现了 BeanPostProcessor 接口的 Bean 的 postProcessBeforeInitialization 方法。
    • 你可以在这个阶段对 Bean 进行一些预处理或修改。
  5. 执行初始化回调 (Initialization Callbacks):

    • 这是 Bean 准备就绪的关键阶段。Spring 会按照特定的顺序调用 Bean 的初始化方法:
      • 如果 Bean 实现了 org.springframework.beans.factory.InitializingBean 接口,会调用其 afterPropertiesSet() 方法。
      • 如果 Bean 定义中指定了 init-method(XML 配置)或使用了 @Bean 的 initMethod 属性(Java Config),会调用指定的方法。
      • 如果 Bean 类中有使用 @PostConstruct 注解的方法,会调用这些方法。
    • 这些方法通常用于执行 Bean 启动时必须完成的任务,比如初始化连接池、加载配置文件、执行一些自检等。
  6. 执行 BeanPostProcessor 的 postProcessAfterInitialization 方法:

    • 在 Bean 完成所有初始化方法调用后,Spring 会调用所有实现了 BeanPostProcessor 接口的 Bean 的 postProcessAfterInitialization 方法。
    • 这个阶段非常重要,Spring 的 AOP 功能通常就是在这个阶段通过创建 Bean 的代理对象来实现的。当你从容器获取 Bean 时,如果它被 AOP 代理了,你拿到的实际上是这个代理对象。(如果这里没明白最后有补充说明😀)
  7. Bean 准备就绪,可以使用 (Bean is Ready for Use):

    • 经过以上步骤,Bean 已经完全配置和初始化完成,可以被应用程序的其他部分使用了。
    • 当你通过 applicationContext.getBean() 获取 Bean 或通过 @Autowired 注入 Bean 时,拿到的就是这个已经准备好的实例(对于单例)。
  8. 容器关闭,执行销毁回调 (Container Shutdown, Destruction Callbacks):

    • 当 Spring 容器关闭时(比如 Web 应用停止,或者调用 applicationContext.close()),Spring 会对那些需要销毁的 Bean(主要是单例 Bean)执行销毁操作。
    • 销毁方法调用的顺序:
      • 如果 Bean 类中有使用 @PreDestroy 注解的方法,会调用这些方法。
      • 如果 Bean 实现了 org.springframework.beans.factory.DisposableBean 接口,会调用其 destroy() 方法。
      • 如果 Bean 定义中指定了 destroy-method(XML 配置)或使用了 @Bean 的 destroyMethod 属性(Java Config),会调用指定的方法。
    • 这些方法通常用于释放 Bean 占用的资源,比如关闭数据库连接、关闭文件流等。

原型 (Prototype) Bean 的生命周期区别:

对于原型 Bean,Spring 容器只负责到步骤 6(执行 postProcessAfterInitialization)为止。一旦 Bean 被创建并交给请求者,Spring 就不再管理它了。步骤 8 的销毁回调不会由 Spring 容器自动执行。你需要自己负责管理原型 Bean 的销毁。

举个例子:

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

// @Component 注解告诉 Spring 这是一个 Bean,默认是 singleton 作用域
@Component
public class MyLifecycleBean implements InitializingBean, DisposableBean {

    private String message; // 这是一个属性,用于演示属性填充

    // --- Bean 生命周期步骤开始 ---

    // 步骤 2: 实例化 (Instantiation)
    // Spring 通过调用 Bean 的构造器来创建 Bean 的实例对象
    public MyLifecycleBean() {
        System.out.println("--- Step 2: MyLifecycleBean: Constructor called (实例化) ---");
        // 此时对象刚创建,属性 message 还是 null
    }

    // 步骤 3: 属性填充 / 依赖注入 (Property Population / Dependency Injection)
    // 如果 Bean 定义中有属性需要设置(比如通过 <property> 或 @Value),或者有依赖需要注入(@Autowired),
    // Spring 会在实例化后调用相应的 Setter 方法或直接设置字段。
    public void setMessage(String message) {
        this.message = message;
        System.out.println("--- Step 3: MyLifecycleBean: Setter for message called (属性填充/依赖注入) ---");
        // 此时 message 属性已经被 Spring 设置了值
    }

    // --- 步骤 4: BeanPostProcessor.postProcessBeforeInitialization 在这里执行 ---
    // Spring 会调用所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法
    // 可以在 Bean 的初始化方法调用前进行一些处理

    // 步骤 5: 执行初始化回调 (Initialization Callbacks)
    // Spring 会按特定顺序调用 Bean 的初始化方法。这些方法标志着 Bean 已经准备好被使用了。

    // 5a: @PostConstruct 注解方法
    // 这是 JSR-250 规范定义的初始化回调,Spring 支持它。
    @PostConstruct
    public void postConstructMethod() {
        System.out.println("--- Step 5a: MyLifecycleBean: @PostConstruct method called ---");
        // 适合在这里执行一些依赖注入完成后,Bean 首次可以使用前的初始化逻辑
    }

    // 5b: InitializingBean 接口的 afterPropertiesSet() 方法
    // 这是 Spring 定义的初始化接口。
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("--- Step 5b: MyLifecycleBean: InitializingBean.afterPropertiesSet() called ---");
        // 适合在这里进行一些依赖注入完成后的验证或初始化操作
        if (this.message == null) {
            this.message = "Default Message from afterPropertiesSet"; // 可以在这里设置默认值
        }
    }

    // 5c: 自定义 init-method (通过 @Bean(initMethod="customInitMethod") 或 XML 配置指定)
    // 这是通过配置指定的初始化方法。
    public void customInitMethod() {
         System.out.println("--- Step 5c: MyLifecycleBean: Custom init-method called ---");
         // 适合在这里执行自定义的初始化逻辑
    }

    // --- 步骤 6: BeanPostProcessor.postProcessAfterInitialization 在这里执行 ---
    // Spring 会调用所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。
    // AOP 代理的创建通常发生在这个阶段。如果 Bean 被代理,这里返回的是代理对象。

    // 步骤 7: Bean 准备就绪,可以使用 (Bean is Ready for Use)
    // Bean 已经完全初始化并配置完成,可以被其他 Bean 注入或直接获取使用了。
    // doSomething() 是一个普通的业务方法,不是生命周期回调,它在 Bean 准备就绪后才能被调用。
    public void doSomething() {
        System.out.println("--- Step 7: MyLifecycleBean: doSomething() called. Message: " + message + " ---");
    }

    // --- Bean 生命周期结束,容器关闭 ---

    // 步骤 8: 容器关闭,执行销毁回调 (Destruction Callbacks)
    // 当 Spring 容器关闭时,会调用 Bean 的销毁方法来释放资源。

    // 8a: @PreDestroy 注解方法
    // 这是 JSR-250 规范定义的销毁回调,Spring 支持它。
    @PreDestroy
    public void preDestroyMethod() {
        System.out.println("--- Step 8a: MyLifecycleBean: @PreDestroy method called ---");
        // 适合在这里释放资源,比如关闭文件流、网络连接等
    }

    // 8b: DisposableBean 接口的 destroy() 方法
    // 这是 Spring 定义的销毁接口。
    @Override
    public void destroy() throws Exception {
        System.out.println("--- Step 8b: MyLifecycleBean: DisposableBean.destroy() called ---");
        // 适合在这里释放资源
    }

    // 8c: 自定义 destroy-method (通过 @Bean(destroyMethod="customDestroyMethod") 或 XML 配置指定)
    // 这是通过配置指定的销毁方法。
    public void customDestroyMethod() {
        System.out.println("--- Step 8c: MyLifecycleBean: Custom destroy-method called ---");
        // 适合在这里执行自定义的清理逻辑
    }

    // --- Bean 生命周期结束 ---
}

如何运行并观察输出?

你需要一个 Spring 容器来运行这个例子。最简单的方式是使用 AnnotationConfigApplicationContext

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 配置类,用于定义 Bean
@Configuration
public class AppConfig {

    @Bean(initMethod = "customInitMethod", destroyMethod = "customDestroyMethod")
    public MyLifecycleBean myLifecycleBean() {
        MyLifecycleBean bean = new MyLifecycleBean();
        bean.setMessage("Hello from Config"); // 通过 Setter 设置属性
        return bean;
    }
}

// 主程序
public class MainApplication {

    public static void main(String[] args) {
        System.out.println("--- Spring Container Starting ---");

        // 创建并启动 Spring 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        System.out.println("--- Spring Container Started ---");

        // 从容器获取 Bean 并使用
        MyLifecycleBean myBean = context.getBean(MyLifecycleBean.class);
        myBean.doSomething();

        System.out.println("--- Using Bean finished ---");

        // 关闭容器,触发销毁回调
        context.close();

        System.out.println("--- Spring Container Closed ---");
    }
}

运行 MainApplication 的 main 方法,你会在控制台看到类似以下的输出顺序:

--- Spring Container Starting ---
--- Step 2: MyLifecycleBean: Constructor called (实例化) ---
--- Step 3: MyLifecycleBean: Setter for message called (属性填充/依赖注入) ---
--- Step 5a: MyLifecycleBean: @PostConstruct method called ---
--- Step 5b: MyLifecycleBean: InitializingBean.afterPropertiesSet() called ---
--- Step 5c: MyLifecycleBean: Custom init-method called ---
--- Spring Container Started ---
--- Step 7: MyLifecycleBean: doSomething() called. Message: Hello from Config ---
--- Using Bean finished ---
--- Step 8a: MyLifecycleBean: @PreDestroy method called ---
--- Step 8b: MyLifecycleBean: DisposableBean.destroy() called ---
--- Step 8c: MyLifecycleBean: Custom destroy-method called ---
--- Spring Container Closed ---

有两处地方很明显可以注意到:步骤中的子步骤(比如初始化阶段的 5a, 5b, 5c 和销毁阶段的 8a, 8b, 8c),他们不是说一个 Bean 必须要有多种方法或接口需要关闭/初始化

它们的含义是:

  1. Spring 提供了多种方式让你定义 Bean 的初始化和销毁逻辑。
    • 你可以通过实现特定的接口 (InitializingBeanDisposableBean)。
    • 你可以通过使用特定的注解 (@PostConstruct@PreDestroy)。
    • 你也可以通过配置(XML 或 @Bean 的 initMethod/destroyMethod 属性)指定一个普通方法作为初始化或销毁方法。
  2. 一个 Bean 可以选择使用其中的一种、多种甚至不使用任何一种方式来定义初始化或销毁回调。
    • 一个简单的 Bean 可能没有任何初始化或销毁逻辑。
    • 一个 Bean 可能只使用了 @PostConstruct 来初始化。
    • 一个 Bean 也可能同时实现了 InitializingBean 接口并且使用了 @PreDestroy 注解。
  3. 如果一个 Bean 定义了多种初始化回调(或多种销毁回调),Spring 会按照一个固定的顺序依次调用它们。

步骤6详解:

首先,我们先简单回顾一下 AOP 的核心思想。

AOP (面向切面编程) 是什么?

AOP 是一种编程范式,它的主要目的是将那些横跨多个模块的功能(称为横切关注点,Cross-cutting Concerns)从核心业务逻辑中分离出来。

常见的横切关注点有:

  • 事务管理 (Transactions): 在方法执行前开启事务,执行后提交或回滚。
  • 日志记录 (Logging): 在方法执行前或后记录日志。
  • 安全检查 (Security): 在方法执行前进行权限验证。
  • 缓存 (Caching): 在方法执行前检查缓存,执行后更新缓存。

如果没有 AOP,这些功能代码会散布在你的业务逻辑代码中,导致代码重复、难以维护。

AOP 通过切面 (Aspect) 来实现这些横切关注点。一个切面通常包含:

  • 通知 (Advice): 实际要执行的代码(比如开启事务、记录日志)。
  • 切入点 (Pointcut): 定义通知要在哪些地方(哪些方法)执行。

Spring AOP 如何实现 AOP?

Spring AOP 默认使用的是动态代理 (Dynamic Proxy)

  • 代理 (Proxy): 代理是一个对象,它“包装”了另一个对象(称为目标对象,Target Object)。当你调用代理对象的方法时,代理可以在调用目标对象的方法之前之后插入额外的逻辑。
  • 动态: 代理对象不是你手动编写的,而是在程序运行时由 Spring 动态生成的。

Spring 会根据你的 AOP 配置(比如 @Aspect 注解、XML 配置),判断哪些 Bean 的哪些方法需要被“增强”(即应用切面)。对于这些 Bean,Spring 不会直接把原始的 Bean 实例注入到其他地方,而是会创建一个代理对象,然后把这个代理对象注入到其他需要依赖它的地方。

现在,我们把 AOP 代理创建和 Bean 的生命周期结合起来:

还记得 Bean 的生命周期步骤吗?

  1. 加载 Bean 定义
  2. 实例化 Bean (调用构造器)
  3. 属性填充/依赖注入 (调用 Setter 或设置字段)
  4. 执行 BeanPostProcessor.postProcessBeforeInitialization
  5. 执行初始化回调 (@PostConstructInitializingBeaninit-method)
  6. 执行 BeanPostProcessor.postProcessAfterInitialization
  7. Bean 准备就绪,可以使用

关键就在第 6 步:执行 BeanPostProcessor.postProcessAfterInitialization

Spring 内部有很多内置的 BeanPostProcessor 实现类,其中就包括负责处理 AOP 的 BeanPostProcessor(比如 AnnotationAwareAspectJAutoProxyCreator,它处理基于注解的 AOP)。

当 Spring 容器创建并初始化好一个 Bean(完成了步骤 1-5)后,就会把这个原始的 Bean 实例传递给所有注册的 BeanPostProcessor 的 postProcessAfterInitialization 方法。

Spring 的 AOP BeanPostProcessor 在接收到这个原始 Bean 实例后,会做以下判断:

  • 检查这个 Bean 的类或者它的方法上是否有需要应用 AOP 的注解(比如 @Transactional@PreAuthorize)?
  • 或者,检查是否有 AOP 配置(比如 XML 中的 <aop:config>)定义了切入点,并且这个 Bean 的方法匹配了这些切入点?

如果判断结果是“是”,这个 Bean 需要被 AOP 增强:

  1. 这个 AOP BeanPostProcessor 会利用 Java 的动态代理机制(或者 CGLIB 库,如果目标对象没有实现接口)为这个原始 Bean 实例创建一个代理对象
  2. 这个代理对象“知道”如何执行原始 Bean 的方法,并且在执行前后会插入 AOP 切面中定义的逻辑(比如事务的开启/提交/回滚)。
  3. 最重要的一点:postProcessAfterInitialization 方法的返回值就是这个新创建的代理对象,而不是原始的 Bean 实例。

如果判断结果是“否”,这个 Bean 不需要被 AOP 增强:

  1. 这个 AOP BeanPostProcessor 什么也不做。
  2. postProcessAfterInitialization 方法直接返回原始的 Bean 实例

所以,当你从 Spring 容器获取一个 Bean(通过 @Autowired 或 getBean())时:

  • 如果这个 Bean 没有被 AOP 代理,你拿到的是它原始的实例
  • 如果这个 Bean  AOP 代理了,你拿到的是 Spring 在 postProcessAfterInitialization 阶段为你创建的那个代理对象

为什么这样做?

因为其他 Bean 依赖的是这个被增强后的功能(比如需要事务支持)。通过注入代理对象,当其他 Bean 调用被增强的方法时,实际上是调用了代理对象的方法。代理对象会先执行事务逻辑,然后再调用原始 Bean 的方法,从而实现了事务的自动管理,而调用者完全不需要关心事务的细节。

举个例子:

假设你有一个 OrderService Bean,它的 placeOrder() 方法需要事务支持,所以你加了 @Transactional 注解。

@Service
public class OrderService {

    @Autowired
    private OrderDao orderDao; // 假设 OrderDao 是另一个 Bean

    @Transactional // 这个方法需要事务
    public void placeOrder(Order order) {
        System.out.println("--- OrderService: placeOrder method called ---");
        // 1. 保存订单到数据库
        orderDao.save(order);
        // 2. 更新库存 (假设这里可能出错)
        // inventoryService.updateStock(...);
        // ... 其他业务逻辑 ...
        System.out.println("--- OrderService: placeOrder method finished ---");
    }
}

@Component
public class AnotherComponent {
    @Autowired
    private OrderService orderService; // 注入 OrderService

    public void processOrder() {
        System.out.println("--- AnotherComponent: Calling placeOrder ---");
        orderService.placeOrder(new Order()); // 调用 OrderService 的方法
        System.out.println("--- AnotherComponent: placeOrder call finished ---");
    }
}

Bean 的生命周期过程:

  1. Spring 容器启动,加载 OrderService 和 AnotherComponent 的 Bean 定义。
  2. 实例化 OrderService 和 AnotherComponent
  3. 为 OrderService 注入 orderDao
  4. 执行 BeanPostProcessor.postProcessBeforeInitialization (对 OrderService 和 AnotherComponent)。
  5. 执行 OrderService 和 AnotherComponent 的初始化回调 (@PostConstruct 等)。
  6. 执行 BeanPostProcessor.postProcessAfterInitialization
    • 当轮到 OrderService 时,Spring 的 AOP BeanPostProcessor 发现 placeOrder 方法上有 @Transactional 注解。
    • 它会为原始的 OrderService 实例创建一个代理对象
    • postProcessAfterInitialization 方法返回这个代理对象
    • 当轮到 AnotherComponent 时,它没有 AOP 配置,postProcessAfterInitialization 直接返回原始的 AnotherComponent 实例
  7. Bean 准备就绪:
    • OrderService 在容器中注册的是那个代理对象
    • AnotherComponent 在容器中注册的是它的原始实例
  8. 依赖注入: 当 Spring 为 AnotherComponent 注入 orderService 时,它从容器中获取 OrderService Bean,拿到的是那个代理对象,并将这个代理对象注入到 AnotherComponent 的 orderService 字段中。
  9. 使用 Bean: 当 AnotherComponent 调用 orderService.placeOrder() 时,实际上是调用了代理对象的 placeOrder 方法。
  10. 代理对象工作: 代理对象拦截了这个调用,先开启一个数据库事务,然后调用原始 OrderService 实例的 placeOrder() 方法。原始方法执行完毕后,代理对象再根据执行结果(是否抛异常)来提交或回滚事务。

所以,BeanPostProcessor.postProcessAfterInitialization 就是 Spring AOP 插入其逻辑、创建代理对象并替换原始 Bean 实例的那个时机。通过这个机制,Spring 实现了在不修改业务代码的情况下,为 Bean 添加事务、安全等横切功能!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值