欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-04 14:55:39
目录两个单例testa testb 互相依赖的实例化过程源码中的实现方式重要流程梳理首先需要明白一点,只有scop为(singleton)单例类型的bean,spring才支持循环依赖。scope为(...

首先需要明白一点,只有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的实例化。

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