Spring 处理循环依赖的机制笔记

Spring 处理循环依赖的机制笔记

1. Spring Bean 生命周期概述

在 Spring 框架中,一个 bean 的生命周期大致包括以下几个步骤:

  1. 实例化:通过构造函数创建 bean 实例。
  2. 属性填充:注入 bean 的依赖。
  3. 初始化前的处理:调用 BeanPostProcessorpostProcessBeforeInitialization 方法。
  4. 初始化:调用 init-method 或者 InitializingBean 接口的 afterPropertiesSet 方法。
  5. 初始化后的处理:调用 BeanPostProcessorpostProcessAfterInitialization 方法。
  6. 最终放入 singletonObjects:完全初始化后的 bean 放入 singletonObjects 缓存。
2. 循环依赖的概念

当存在循环依赖时,意味着两个或多个 bean 相互依赖对方。例如,bean A 依赖 bean B,而 bean B 也依赖 bean A。

3. Spring 如何处理循环依赖

在处理循环依赖时,Spring 通过提前暴露部分初始化的 bean 实例,使得每个 bean 都能在初始化过程中获得所需的依赖。

具体步骤:
  1. 实例化第一个 bean

    • 假设有两个 bean A 和 B,它们相互依赖。
    • Spring 首先实例化 bean A。
  2. 注入依赖

    • 在注入 bean B 到 bean A 时,发现 bean B 尚未初始化。
    • 此时 Spring 会尝试从缓存中获取 bean B。
  3. 创建 bean B 的实例

    • 由于 bean B 也依赖 bean A,Spring 会在 singletonFactories 中创建 bean B 的工厂,然后创建 bean B 的实例。
  4. 注入依赖

    • 此时 bean B 的实例需要 bean A,但由于 bean A 已经在 earlySingletonObjects 中存在,所以 Spring 可以从 earlySingletonObjects 中获取 bean A 的部分初始化版本,并注入到 bean B 中。
  5. 完成 bean B 的初始化

    • 完成 bean B 的所有初始化步骤,并将其放入 singletonObjects
  6. 完成 bean A 的初始化

    • 现在 bean A 有了完全初始化的 bean B,可以继续完成自己的初始化,并最终放入 singletonObjects
4. 三级缓存的角色
  • singletonObjects:存放完全初始化的 bean。
  • earlySingletonObjects:存放部分初始化的 bean,即已经实例化并注入了依赖,但还没有调用 postProcessBeforeInitializationinit-method
  • singletonFactories:存放创建部分初始化 bean 的工厂。
5. 为什么没有再出现循环依赖的问题
  1. 部分初始化的 bean

    • 在注入依赖时,Spring 会使用部分初始化的 bean,这意味着 bean 在注入时已经完成了实例化和依赖注入,但还没有调用 init-method 或者 BeanPostProcessorpostProcessBeforeInitialization 方法。
  2. 逐步推进

    • 通过这种方式,Spring 保证了每个 bean 都能逐步推进其初始化过程。一旦某个 bean 完成了初始化,它就会被放入 singletonObjects,这样其他 bean 在后续的初始化过程中就可以使用已经完全初始化的 bean。
  3. 避免无限循环

    • 通过在部分初始化阶段就注入 bean,Spring 避免了无限循环的发生。每个 bean 都有机会在初始化过程中获得所需的依赖,即使这些依赖一开始只是部分初始化的。
6. 示例

假设我们有两个 bean A 和 B,它们之间存在循环依赖关系:

public class A {
    private B b;

    public A(B b) {
        this.b = b;
    }

    public void init() {
        // 进行一些初始化操作
    }
}

public class B {
    private A a;

    public B(A a) {
        this.a = a;
    }

    public void init() {
        // 进行一些初始化操作
    }
}

在这个例子中:

  1. 实例化 A:Spring 首先实例化 bean A。
  2. 注入 B 到 A:在注入 bean B 时,发现 B 尚未初始化。此时 Spring 会尝试初始化 B。
  3. 实例化 B:Spring 开始实例化 bean B。
  4. 注入 A 到 B:在注入 bean A 时,发现 A 已经部分初始化了(即已经被实例化,但尚未完成其他初始化步骤)。此时 Spring 会从 earlySingletonObjects 中获取 A 的部分初始化版本,并注入到 B 中。
  5. 完成 B 的初始化:B 现在有了部分初始化的 A,可以继续自己的初始化过程,直到完全初始化,并放入 singletonObjects
  6. 完成 A 的初始化:A 现在有了完全初始化的 B,可以继续自己的初始化过程,直到完全初始化,并放入 singletonObjects

通过这种方式,即使存在循环依赖,Spring 也能够确保每个 bean 都能够完成其初始化过程,而不会陷入无限循环。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值