彻底搞懂 Spring 循环依赖

38 阅读4分钟
什么是循环依赖

互相引用,形成一个环。

依赖方式

图片

Bean的创建

根据直接互相依赖展示流程

图片

要解决循环依赖,就得解决第二次A创建过程

Spring 如何解决的?

依赖的三级缓存

// 一级
/** Cache of singleton objects: bean name to bean instance. */
  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 三级
  /** Cache of singleton factories: bean name to ObjectFactory. */
  private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

// 二级
  /** Cache of early singleton objects: bean name to bean instance. */
  private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

示例代码

@Component
public class A {
  @Autowired
  private B b;
}

@Component
public class B {
  @Autowired
  private A a;
}
@ComponentScan(basePackages = {"com.wl.anination"})
public class Config {
}
Spring 代码梳理

具体创建 bean 的过程此处不再讲解,请参考 文章 Spring 源码之 Bean 的创建

Spring源码之Bean的创建(一)

Spring 源码之 Bean的创建(二)

  1. 创建A
  • Object sharedInstance = getSingleton(beanName); // 从容器获取 【AbstractBeanFactory.doGetBean】

    获取为空:

    beforeSigletonCreation,记录当前bean正在创建

    markBeanAsCreated,放置于alreadyCreated

  • doCreateBean创建A

    判断正在创建中:加入三级缓存

    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    
  • populateBean 设置属性

    发现需要设置属性B,从容器重获取,首次没有,此时创建B,到 2

  • 获取到B 设置属性值

  1. 创建B,流程同A

Object sharedInstance = getSingleton(beanName); // 从容器获取 【AbstractBeanFactory.doGetBean】

获取为空:

beforeSigletonCreation,记录当前bean正在创建

markBeanAsCreated,放置于alreadyCreated

doCreateBean创建A

判断正在创建中:加入三级缓存

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

populateBean 设置属性

发现需要设置属性A,从容器获取

getSingleton:从一级取,从二级取,最后从三级取,三级取到时放到二级缓存并删除三级缓存

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    // 一级缓存取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      // 一级缓存没有且正在创建,从二级取
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
        // 二级也没有,加锁
        synchronized (this.singletonObjects) {
          // Consistent creation of early reference within full singleton lock
          // 从一级取
          singletonObject = this.singletonObjects.get(beanName);
          if (singletonObject == null) {
            // 从二级取
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null) {
              // 三级取
              ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
              if (singletonFactory != null) {
                singletonObject = singletonFactory.getObject();
                // 获取对象,删除三级缓存,放至二级缓存
                this.earlySingletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
              }
            }
          }
        }
      }
    }
    return singletonObject;
  }
  • addSigleton

    放至一级缓存、删除二级缓存三级缓存

  • 此时B创建完成,完整的B对象,存于一级缓存,返回到A创建设置B属性

到此看到循环解决办法,二级缓存可以解决吗?我试着改了下代码,当前示例是没有问题的

代码改动:设计2处改动

getSigleton

图片

addSingletonFactory:调用此处时改成只放二级缓存

图片

可以正常输出,说明二级缓存是可以解决循环依赖的

图片

我们添加 C 类,此类添加aop拦截,就会报错

图片

为什么会有这样的错?

A 创建时,设置B 属性,B创建,此时引用A(前期创建),二级缓存放B 前期引用,B 被动态代理,此时B有两个状态;

初始化在前,动态代理在后,可能会改变对象,所以在属性设置时,获取三级缓存,通过lambda直接生成代理对象。

两级缓存可以解决普通依赖,对于代理的对象就不可以了。

三级缓存是何时放进去的?

一级缓存:完全创建后,调用 addSigleton ,删除二、三级缓存

二级缓存:需要获取bean,调用getSingleton,删除三级缓存,对象放于二级缓存

三级缓存:实例化bean,调用addSingletonFactory,放于三级缓存

来自公众号:一个程序猿的笔记