跳到主要内容

循环依赖和三级缓存

循环依赖

  • 循环依赖: 多个对象存在互相组合的环状依赖关系,A对象中有B属性,B对象中有A属性,让 Spring 无法为其直接注入所需依赖,在 Spring 中只有单例 Bean 会进行初始化加载
  • 解决循环依赖的前置条件
    1. 出现循环依赖的 Bean 必须是单例
    2. 依赖注入的方式不能全是构造器注入的方式
      循环依赖与依赖注入方式

简单的循环依赖(没有AOP)

@Component
public class A {
// A中注入了B
@Autowired
private B b;
}

@Component
public class B {
// B中也注入了A
@Autowired
private A a;
}
  • 调用 getBean(): 创建一个新的 Bean,或者从缓存中获取到已经被创建的对象

  • 调用 getSingleton(beanName): 这个方法会调用 getSingleton(beanName, true) 方法,从缓存中尝试获取 Bean,缓存中没有时,则会进入另一个重载方法 getSingleton(beanName, singletonFactory)

    public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
    }
  • 调用 getSingleton(beanName, singletonFactory): 这个方法实际上就是用来创建 bean 的,通过 createBean 方法返回的 Bean 最终被放到了一级缓存(即单例池中)

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {

    // ....
    // 省略异常处理及日志
    // ....

    // 在单例对象创建前先做一个标记
    // 将beanName放入到singletonsCurrentlyInCreation这个集合中
    // 标志着这个单例Bean正在创建
    // 如果同一个单例Bean多次被创建,这里会抛出异常
    beforeSingletonCreation(beanName);
    boolean newSingleton = false;
    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    if (recordSuppressedExceptions) {
    this.suppressedExceptions = new LinkedHashSet<>();
    }
    try {
    // 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
    singletonObject = singletonFactory.getObject();
    newSingleton = true;
    }
    // ...
    // 省略catch异常处理
    // ...
    finally {
    if (recordSuppressedExceptions) {
    this.suppressedExceptions = null;
    }
    // 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
    afterSingletonCreation(beanName);
    }
    if (newSingleton) {
    // 添加到一级缓存singletonObjects中
    addSingleton(beanName, singletonObject);
    }
    }
    return singletonObject;
    }
    }
  • 调用 addSingletonFactory 方法: 在完成 Bean 的实例化后,属性注入之前将 Bean 包装成一个工厂添加进三级缓存

    // 这里传入的参数也是一个lambda表达式,() -> getEarlyBeanReference(beanName, mbd, bean)
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
    if (!this.singletonObjects.containsKey(beanName)) {
    // 添加到三级缓存中
    this.singletonFactories.put(beanName, singletonFactory);
    this.earlySingletonObjects.remove(beanName);
    this.registeredSingletons.add(beanName);
    }
    }
    }

    简单的循环依赖(没有AOP)

循环依赖解决总结

  • 当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

三级缓存

  • 三级缓存:

    1. 一级缓存: 用于存放已经实例化、初始化完成的单例 Bean,单例池-singletonObjects
    2. 二级缓存: 用于存放已经实例化,但未进行属性注入及初始化的 Bean,保证一个类多次循环依赖时仅构建一次(保证单例),早期曝光对象池 bean 池-earlySingletonObjects
    3. 三级缓存: 提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的 Bean。用于存放该 Bean 的 BeanFactory,当加载一个 Bean 时,会先将该 Bean 包装为 BeanFactory 放入三级缓存,早期曝光 bean 工厂池-singletonFactories

    三级缓存

创建 Bean 的流程

  • 创建 Bean 时会先将该 Bean 的 BeanFactory 放入三级缓存,以防止循环依赖问题,当存在A、B两个 Bean 循环依赖时,流程如下

    1. 先创建 Bean A,实例化 Bean A 并包装为 BeanFactory 放入三级缓存中
    2. 给 Bean A 进行属性填充时检查依赖,发现 Bean B 没有加载过,此时先去加载 Bean B
    3. Bean B 创建过程中首先也要包装成 BeanFactory 放入三级缓存,填充属性时则从三级缓存中的 BeanAFactory 获取 Bean A 填充进去
    4. 获取 Bean A 通过 ObjectFactory.getObject() 方法,该方法调用 getEarlyBeanReference() 方法,创建 Bean A 的代理并从三级缓存中删除 Bean A,放入二级缓存
    5. Bean B 初始化完成后放入一级缓存,Bean A 继续执行初始化,初始化完成后比较二级缓存中的 Bean A 和一级缓存中的是否一致,一致则从二级缓存中删除,放入一级缓存

    创建 Bean 的流程