Spring如何解决循环依赖

1. 什么是循环依赖?

循环依赖指的是两个或多个 bean 互相依赖,形成一个闭环。例如:

  • A 依赖于 B
  • B 依赖于 C
  • C 又依赖于 A

这种关系导致在实例化时会出现死循环,无法完成对象的创建。

2. Spring 如何解决循环依赖?

Spring 主要通过三级缓存(三级 Map)机制来解决循环依赖问题。以下是三级缓存的具体机制:

  1. Singleton Objects 一级缓存:保存完全初始化好的单例 bean 对象。
  2. Early Singleton Objects 二级缓存:保存早期曝光的单例对象,这些对象只是实例化了,但还没有完成依赖注入。
  3. Singleton Factories 三级缓存:保存对象工厂,可以通过工厂获取早期的 bean 实例。
具体实现步骤:
  1. 创建 bean 实例
    • 当 Spring 容器遇到一个 bean 的创建请求时,首先会尝试从一级缓存 singletonObjects 中获取该 bean。如果获取不到,再继续向下寻找。
  2. 存储 Bean 对象的引用
    • Spring 会首先创建 bean 的原始实例(通过构造函数或工厂方法进行实例化),但未进行依赖注入。这时将该实例的引用存储在三级缓存 singletonFactories 中,以便循环引用的情况下其他 bean 可以提前访问到这个未完成的 bean 实例。
  3. 依赖注入和初始化
    • 对 bean 进行属性依赖注入时,如果该 Bean 依赖了其他 bean,那么:
      • 尝试从一级缓存 singletonObjects 获取依赖的 bean。
      • 如果获取不到,则从二级缓存 earlySingletonObjects 获取。
      • 如果仍获取不到,则通过三级缓存 singletonFactories 中保存的 ObjectFactory 创建依赖,并将其移到二级缓存 earlySingletonObjects,过程中可能还会涉及到代理处理(如 AOP)。
    • 完成属性依赖注入后,进行初始化。如果一个 bean 依赖的其他 bean 还未完全初始化,Spring 可以在三级缓存中找到它们的早期引用,避免死循环。
  4. 完成 Bean 的创建
    • 在完成依赖注入和初始化后,将完全初始化的 bean 放入一级缓存 singletonObjects,并将其从二级缓存和三级缓存中移除。

3. 示例代码分析

假设我们有两个互相依赖的类:

@Component
public class A {

    @Autowired
    private B b;

    public A() {
        System.out.println("A instance created");
    }

    public void doSomething() {
        b.doSomething();
    }
}

@Component
public class B {

    @Autowired
    private A a;

    public B() {
        System.out.println("B instance created");
    }

    public void doSomething() {
        a.doSomething();
    }
}

在这种情况下,Spring 的三级缓存机制将会起作用:

  1. A实例创建

    • Spring 首先创建 A 的实例。
    • A 的构造函数执行,但此时 A 还未完成依赖注入,它会将 A 的早期引用放入三级缓存。
  2. B实例创建

    • 在 A 的依赖注入过程中,需要注入 B,此时 Spring 创建 B 的实例。
    • B 的构造函数执行,但此时 B 也还未完成依赖注入,它会将 B 的早期引用放入三级缓存。
  3. 再次注入A

    • 在 B 的依赖注入过程中,需要注入 A。此时Spring 会尝试从三级缓存中获取 A 的早期引用并注入 B
  4. 完成依赖注入

    • A 和 B 的依赖注入完成后,Spring 将 A 和 B 放入一级缓存,并将它们从三级缓存中移除。

4. 注意事项

  • 构造函数注入的限制:如果两个类通过构造函数互相注入,三级缓存机制将无法解决这种循环依赖,因为在构造函数调用时就需要实例化依赖的对象。这种情况下,需要考虑通过其他方式(比如使用 @PostConstruct 或者 Setter 方法)来处理依赖注入。

  • 非单例模式:Spring 无法解决 prototype 范围的 bean 的循环依赖问题。prototype 范围的 bean 每次获取都是新实例,无法利用缓存机制解决循环依赖。如果涉及 prototype 范围的 bean,建议重构代码来避免循环依赖。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值