关于Spring源码是如何解决Bean的循环依赖

 更新时间:2021年12月14日 10:54:13   作者:弦上的梦  
这篇文章主要介绍了关于Spring源码是如何解决Bean的循环依赖,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

首先需要明白一点,只有scop为(singleton)单例类型的Bean,spring才支持循环依赖。

scope为(prototype)原型类型的Bean是不支持的,它每次调用都会创建一个新的实例,spring 在实例化bean的时候会先实例化bean的各种属性依赖,如果TestA TestB是原型类型且相互依赖则会出现new TestA 的时候,先new TestB,然后new TestB的时候又去new TestA会出现无限套娃的情况。

两个单例testA testB 互相依赖的实例化过程

Spring容器创建单例“testA”bean

首先根据无参构造器创建bean,并暴露一个“ObjectFactory”用于返回一个提前暴露正在创建中的bean,并将“testA”标识符放到“当前创建bean池”,然后进行setter注入“testB”。

Spring容器创建单例“testB”bean

首先根据无参构造器创建bean,并暴露一个“ObjectFactory”用于返回一个提前暴露正在创建中的bean,并将“testB”标识符放到“当前创建bean池”,然后进行setter注入“testA”,此时由于通过 暴露"ObjectFactory" 已提前暴露了一个正在创建中的"testA" bean,所以直接注入,完成testB的创建,注入testA中,再完成testA的创建。

源码中的实现方式

首先了解一下创建Bean过程中最重要的三个map

以下三个Map均来自于 DefaultSingletonBeanRegistry

Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  • singletonObjects:用于保存BeanName和创建bean实例之间的关系,bean name 一> bean instance。
  • singletonFactories:用于保存BeanName和创建bean的工厂之间的关系,bean name 一>ObjectFactory。
  • earlySingletonObjects:也是保存BeanName和创建bean实例之间的关系,与singletonObjects的不同之处在于,当一个单例bean被放到这里面后,那么当bean还在创建过程中,就可以通过getBean方法获取到了,其目的是用来检测循环引用。

总结:后面两个Map实际上就是为了辅助第一个Map缓存Bean的实例,完成后数据就在后面两个Map中清掉了。

测试代码:

// 1. 引入依赖,springboot项目只需要这一个依赖即可测试
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
// 2. 两个测试类
@Component
public class TestA {
    @Autowired
    private TestB testB;
}
@Component
public class TestB {
    @Autowired
    private TestA testA;
}

注意:下面所有的方法都只是源码的部分截取,把我认为重要的逻辑放在这里的,大家阅读时,可提前在IDE中打开文中提到的几个类,在相应方法处,打上断点可以直接调试,bean的实例化过程就一目了然了。

1. AbstractBeanFactory类中getBean方法

public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
  throws BeansException {
 return doGetBean(name, requiredType, args, false);
}

2. AbstractBeanFactory类中doGetBean方法

// 
// 2.1 从缓存中获取实例Bean,第一次肯定没有,为null
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else{
 // Create bean instance.
 if (mbd.isSingleton()) {
  // 2.2 获取缓存中的实例
  sharedInstance = getSingleton(beanName, () -> {
   try {
    // 2.3 调用创建Bean实例的方法
    return createBean(beanName, mbd, args);
   } 
  });
  beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
 }
}

3. DefaultSingletonBeanRegistry类中getSingleton方法

2.1调用的就是这里的3.1

// 3.1
@Override
@Nullable
public Object getSingleton(String beanName) {
 return getSingleton(beanName, true);
}
// 3.2
@Nullable
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;
}

3.1和3.2后面会反复获取的,第一次因为isSingletonCurrentlyInCreation(beanName)返回false,所以返回null

4. DefaultSingletonBeanRegistry类中getSingleton方法

获取ObjectFactory,2.2就是调用的这里

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
 synchronized (this.singletonObjects) {
  Object singletonObject = this.singletonObjects.get(beanName);
  if (singletonObject == null) {
   beforeSingletonCreation(beanName);
   boolean newSingleton = false;
   try {
    // 4.0.1
    singletonObject = singletonFactory.getObject();
    newSingleton = true;
   }
   finally {
    afterSingletonCreation(beanName);
   }
   if (newSingleton) {
    // 4.0.2
    addSingleton(beanName, singletonObject);
   }
  }
  return singletonObject;
 }
}
protected void addSingleton(String beanName, Object singletonObject) {
 synchronized (this.singletonObjects) {
  this.singletonObjects.put(beanName, singletonObject);
  this.singletonFactories.remove(beanName);
  this.earlySingletonObjects.remove(beanName);
  this.registeredSingletons.add(beanName);
 }
}

这面重点分析beforeSingletonCreation 、afterSingletonCreation 和 addSingleton这三个方法

4.1 DefaultSingletonBeanRegistry 中 beforeSingletonCreation方法 和 afterSingletonCreation方法

protected void beforeSingletonCreation(String beanName) {
 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
 }
}
protected void afterSingletonCreation(String beanName) {
 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
  throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
 }
}

重点:这两个方法的目的就是为 singletonsCurrentlyInCreation 这个set集合添加和删除当前创建的Bean,为后续处理做铺垫,addSingleton方法主要是对Bean缓存map的维护。

4.2 现在回到第4步的4.0.1的方法中,singletonObject = singletonFactory.getObject();这行代码就是正真获取实例对象的地方,singletonFactory 是怎么拿到的呢,这就要回到第2步的2.3步骤中,通过createBean方法返回了ObjectFactory类型的singletonFactory,下面看createBean是如何创建Bean的:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {  
 try {
  Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  return beanInstance;
 } 
}
// 看过一些spring源码的都应该明白spring真正做事情的都是以doXXX开头的,这里也不例外
// 相信大家都已经明白真正创建Bean是由doCreateBean方法实现的,下面我们继续分析这个方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
 BeanWrapper instanceWrapper = null;
 if (instanceWrapper == null) {
  instanceWrapper = createBeanInstance(beanName, mbd, args);
 }
 Object bean = instanceWrapper.getWrappedInstance();
 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences
  && isSingletonCurrentlyInCreation(beanName));
 if (earlySingletonExposure) {
  addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
 }
 Object exposedObject = bean;
 try {
  // 4.3 填充Bean的属性,依赖bean就是这里初始化的
  populateBean(beanName, mbd, instanceWrapper);
  exposedObject = initializeBean(beanName, exposedObject, mbd);
 }
 if (earlySingletonExposure) {
  // 4.4 再次获取缓存中的实例,注意这里可以从两个缓存处获取,第一个是earlySingletonObjects map,第二个是singletonFactories map获取
  Object earlySingletonReference = getSingleton(beanName, false);
  if (earlySingletonReference != null) {
   if (exposedObject == bean) {
    exposedObject = earlySingletonReference;
   }
  }
 }
 return exposedObject;
}
// isSingletonCurrentlyInCreation方法
public boolean isSingletonCurrentlyInCreation(String beanName) {
 return this.singletonsCurrentlyInCreation.contains(beanName);
}
// addSingletonFactory
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);
 }
}

重要流程梳理

1.doCreateBean中主要分为两部分

第一部分通过instanceWrapper得到BeanFactory的实例,内部由反射实现,这里我们不多做分析,变量earlySingletonExposure,它由三部分得到,前面两个都很容易理解,第三部分则出现了我们在4.1中做铺垫的集合 singletonsCurrentlyInCreation。

由于在4.1中已经设置了,所以earlySingletonExposure肯定为true,因此执行addSingletonFacatory为singletonFactories map赋值,完成了beanName -> ObjectFactory的映射

2.populateBean方法中

则会完成对Bean依赖属性的注入,因此代码走到4.3的时候,testA的创建就停止了,会回到第一步去获取testB,然后又是对testB的创建,最后会再次走到4.3,完成testA 和 testB 的ObjectFactory的映射,即将它们放入 singletonFactories map缓存中。

3.创建testB 再次走到4.3的时候

又会去初始化testB的依赖 testA,此时会再次去第一步获取,再次走到2.1的时候,因为testA的ObjectFactory是有值的,所以通过它能够获取到testA 的singletonObject,此时就把testA 的实例放入了 earlySingletonObjects中,只不过此时的testA实例是不完整的,还没有完成属性testB依赖的初始化。

最后返回testA的singletonObject引用,完成testB对其依赖testA的初始化,然后再去 4.4 获取testB的缓存,这里依旧是没有的,然后返回到4.0.2处,将testB加入singletonObjects map缓存,并移除testB在singletonFactories中的缓存,这里testB 在 earlySingletonObjects中实际上是没有值的,当然有的话也会移除,因为singletonObjects 中已经拿到值了,所以另外两个辅助map就不用保留数据了。

4.上面已经完成testB的初始化

并放入singletonObjects,缓存了,继续走,就又回到了4.3,继续完成对testA的创建,走到4.4的时候,继续去缓存中获取testA,因为之前已经把testA放入earlySingletonObjects map中了,所以4.4是直接能够获取到testA的实例的。

5.继续走,就又来到了4.0.2

不过这次是针对testA的,addSingleton方法中会把testA的实例给放入singletonObjects map缓存中,同时移除singletonFactories 和 earlySingletonObjects map缓存的testA,完成testA和testB的实例化。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Spring Boot Admin微服务应用监控的实现

    Spring Boot Admin微服务应用监控的实现

    这篇文章主要介绍了Spring Boot Admin微服务应用监控,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Spring security密码加密实现代码实例

    Spring security密码加密实现代码实例

    这篇文章主要介绍了Spring security密码加密实现代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • 深入了解Java数据结构和算法之堆

    深入了解Java数据结构和算法之堆

    这篇文章主要为大家介绍了Java数据结构和算法之堆 ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • Java语言描述存储结构与邻接矩阵代码示例

    Java语言描述存储结构与邻接矩阵代码示例

    这篇文章主要介绍了Java语言描述存储结构与邻接矩阵代码示例,涉及Java存储结构,邻接矩阵,邻接表的介绍与比较,然后分享了邻接矩阵的Java实现等相关内容,具有一定借鉴价值,需要的朋友可以参考。
    2017-11-11
  • 微服务实战之怎样提升springboot服务吞吐量

    微服务实战之怎样提升springboot服务吞吐量

    这篇文章主要介绍了微服务实战之怎样提升springboot服务吞吐量方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • JAVAE中servlet的概念及使用示例详解

    JAVAE中servlet的概念及使用示例详解

    servlet是一种实现动态页面的技术,他是由tomcat提供给程序员的一组API可以帮助程序员开发一个web程序,这篇文章主要介绍了JAVAE中servlet的概念及使用,需要的朋友可以参考下
    2024-05-05
  • Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable)

    Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable)

    本文主要介绍了Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Spring boot中@Conditional和spring boot的自动配置实例详解

    Spring boot中@Conditional和spring boot的自动配置实例详解

    本文通过实例给大家介绍了Spring boot中@Conditional和spring boot的自动配置,需要的朋友可以参考下
    2018-05-05
  • IDEA生成patch和使用patch的方法实现

    IDEA生成patch和使用patch的方法实现

    比如你本地修复的 bug,需要把增量文件发给客户,很多场景下大家都需要手工整理修改的文件,并整理好目录,这个很麻烦,那有没有简单的技巧呢?本文主要介绍了IDEA生成patch和使用patch的方法实现,感兴趣的可以了解一下
    2023-08-08
  • Mac Book中Java环境变量设置的方法

    Mac Book中Java环境变量设置的方法

    本文给大家介绍mac book 中设置java环境变量的方法,非常不错,具有参考借鉴价值,需要的朋友参考下
    2017-04-04

最新评论