关于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的实例化。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
上一篇: Pandas按周/月/年统计数据介绍