哪些依赖情况?

Spring 将循环依赖分成三种情况,并且只能处理其中的一种情况:

  1. 构造器循环依赖;解决不了
  2. setter 循环依赖;可以解决 √
  3. prototype 范围的依赖处理;解决不了

    三级缓存?

    | 缓存 | 作用 | 备注 | | —- | —- | —- | | singletonObjects | 存放已经创建完成,并且属性也注入完毕的对象 | bean name - instance | | earlySingletonObjects | 存放已经创建完成,属性还未注入完毕的对象 | bean name - instance | | singletonFactories | 存放工厂对象 | bean name - ObjectFactory |

还有一些缓存也比较重要:

缓存 作用 备注
singletonsCurrentlyInCreation 当前正在创建的 bean 的名称集合
alreadyCreated 已经创建的 bean 的名称集合

循环依赖演示

使用三级缓存也不能解决所有的循环依赖,如果使用构造器注入属性,这种循环依赖只能抛出异常。

比如下面的代码:

  1. @Component
  2. public class Student {
  3. private Teacher teacher;
  4. public Student(Teacher teacher) {
  5. this.teacher = teacher;
  6. }
  7. }
  1. @Component
  2. public class Teacher {
  3. private Student student;
  4. public Teacher(Student student) {
  5. this.student = student;
  6. }
  7. }

测试

  1. @DisplayName("测试循环依赖")
  2. @Test
  3. public void cycle() {
  4. AnnotationConfigApplicationContext context
  5. = new AnnotationConfigApplicationContext("cn.lichenghao.entity");
  6. }

报错:

  1. Requested bean is currently in creation: Is there an unresolvable circular reference?

如果修改下注入方式为属性注入的话,就没问题如下所示:

  1. @Component
  2. public class Student {
  3. @Autowired
  4. private Teacher teacher;
  5. }
  1. @Component
  2. public class Teacher {
  3. @Autowired
  4. private Student student;
  5. }

实现流程解析

拿上面的代码做演示。有如下两个有依赖关系的单例对象。

  1. @Component
  2. public class Student {
  3. @Autowired
  4. private Teacher teacher;
  5. }
  6. @Component
  7. public class Teacher {
  8. @Autowired
  9. private Student student;
  10. }

对象创建过程如下:
创建 Student 对象:首先 「getBean —> doGetBean —> getSingleton」 student ,没有返回 null
创建 Student 对象:student 标记📌为已创建,加入 alreadyCreated 、 singletonsCurrentlyInCreation 缓存
创建 Student 对象:开始创建,首先 「createBeanInstance」 student 对象,加入三级缓存中
创建 Student 对象:然后「populateBean」 填充 student 属性
创建 Student 对象:发现属性 Teacher ,发现 getBean —> doGetBean —> getSingleton 缓存中没有,就去创建 Teacher 对象

创建 Teacher 对象:teacher 标记📌为已创建,加入 alreadyCreated 、singletonsCurrentlyInCreation 缓存
创建 Teacher 对象:开始创建,首先 「createBeanInstance」teacher 对象,加入三级缓存中
创建 Teacher 对象:然后「populateBean」 填充 teacher 属性
创建 Teacher 对象:发现依赖属性 student ,发现 getBean —> doGetBean —> getSingleton 去一级、二级缓存中没有找到,三级缓存中找到了,得到 student 对象并将其从三级缓存中移动到二级缓存
创建 Teacher 对象:「initializeBean」初始化 teacher 对象,最终完成对象的创建。将 teacher 对象从三级缓存移动到一级缓存,并从 singletonsCurrentlyInCreation 缓存中移除

创建 Student 对象:回到 student 依赖 teacher 属性。拿到 teacher 对象后继续填充属性
创建 Student 对象:「initializeBean」初始化 student 对象,最终完成 student 对象创建。将 student 对象从二级缓存中移动到一级缓存,并从 singletonsCurrentlyInCreation 缓存中移除

主要实现原理

从流程的解析中也可以看出:

Spring 依靠三级缓存解决依赖的最重要的一点在于,Bean 对象的创建是有个一个过程的,先实例化、属性赋值、再初始化,期间还有其方法等等,Spring 利用这些过程的中间态实现对循环依赖的处理。

构造器缓存依赖就行了,构造器是实例化的第一步,没办法暴露bean;同理 prototype 下也不行,因为 Spring 容器不进行缓存 prototype 作用域的bean,因此无法提前暴露一个创建中的 bean。

主要源码解析

缓存获取对象

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton

  1. @Nullable
  2. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  3. // Quick check for existing instance without full singleton lock
  4. // 从单例bean缓存中查找。称为:第一级缓存,用于存放完全初始化好的bean
  5. Object singletonObject = this.singletonObjects.get(beanName);
  6. // 从"bean状态:创建中"的缓存中查找。
  7. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  8. // 从早期单例bean缓存中查找。称为:第二级缓存,提前曝光的单例对象的cache,存放原始的bean对象(尚未填充属性),用于解决循环依赖
  9. singletonObject = this.earlySingletonObjects.get(beanName);
  10. // 如果允许引用的话
  11. if (singletonObject == null && allowEarlyReference) {
  12. // 同步操作
  13. synchronized (this.singletonObjects) {
  14. // Consistent creation of early reference within full singleton lock
  15. singletonObject = this.singletonObjects.get(beanName);
  16. if (singletonObject == null) {
  17. singletonObject = this.earlySingletonObjects.get(beanName);
  18. if (singletonObject == null) {
  19. // 从singletonFactories缓存中判断。称为:第三级缓存
  20. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  21. if (singletonFactory != null) {
  22. // 获取对象
  23. singletonObject = singletonFactory.getObject();
  24. // 加入二级缓存
  25. this.earlySingletonObjects.put(beanName, singletonObject);
  26. // 去掉三级缓存
  27. this.singletonFactories.remove(beanName);
  28. }
  29. }
  30. }
  31. }
  32. }
  33. }
  34. return singletonObject;
  35. }

这块代码要从缓存中得到对象。如果从第三级缓存中得到目标对象,该对象缓会从三级缓存中升级到二级缓存中。

对象加入三级缓存

那么在什么情况下对象会到第三级缓存中 ?
在对象创建的过程中,实例化后,还未初始化之前这个空隙。如下代码所示:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
实例化以后就将对象暴露给 IOC 容器,以便其他依赖的对象使用。

  1. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  2. isSingletonCurrentlyInCreation(beanName));
  3. if (earlySingletonExposure) {
  4. if (logger.isTraceEnabled()) {
  5. logger.trace("Eagerly caching bean '" + beanName +
  6. "' to allow for resolving potential circular references");
  7. }
  8. // !!! 单例 Bean 对象已经实例化,属性还没处理,可以通过引用找到堆上的这个对象
  9. // 将这个状态下的bean放到三级缓存中。表示可以从三级缓存中拿到这个已经创建但是还没有补充完属性的对象
  10. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  11. }
  1. protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  2. Assert.notNull(singletonFactory, "Singleton factory must not be null");
  3. synchronized (this.singletonObjects) {
  4. if (!this.singletonObjects.containsKey(beanName)) {
  5. this.singletonFactories.put(beanName, singletonFactory);
  6. this.earlySingletonObjects.remove(beanName);
  7. this.registeredSingletons.add(beanName);
  8. }
  9. }
  10. }