【八】Spring IoC 最全源码详解之bean的循环依赖
先有鸡还是先有蛋,这是一个问题。
目录
1. 什么是循环依赖
你中有我,我中有你,就是循环依赖。举个例子,本文使用的项目中“yadang”与“xiawa”这两个bean就是循环依赖的(参考附录)。Class Adam中的Eve eve属性依赖Spring框架自动注入,同样Class Eve中的Adam adam属性也依赖于框架的自动注入。并且,Adam和Eve都是单例的。这种情况下,就构成了两个对象的循环依赖。
2. Spring针对循环依赖的解决方案
二话不说,先来一张图描述Spring对于循环依赖的解决方案。
如果您是按照本系列文章的序号依次读完再到这里的,那么根据上面这张图你就已经能看透Spring框架处理循环依赖的方式了,强烈建议您不要看下文,自己根据上图和写个demo debug分析分析印象更深。
如果您仅仅想知道Spring是如何做到的,那我们就开始吧。
2.1 亚当的登场
假设目前“亚当”和“夏娃”这两个bean都还未曾创建,现在流程走到亚当的创建流程(蓝色流程)。因为亚当创建之前不曾有亚当创建出来被容器托管,故流程会很顺利的走到createBean。在createBean之前,会通过beforeSingletonCreation在bean工厂的singletonsCurrentlyInCreation Set集合中添加亚当这个bean的beanName——“yadang”。
随后流程陷入doCreateBean方法中,首先通过createBeanInstance创建出亚当这个对象adamObj,请注意目前亚当它目前长这样:
[code]adam: eve : null
接着流程通过addSingletonFactory,如果在singletonObjects中没有找到“yadang”这个对象,那么就尝试在bean工厂的singletonFactories中进行如下步骤:
[code]1.添加<yadang, singletonFactory>到中singletonFactories中,元素的key是亚当的beanName,value是创造该对象所用的bean工厂。 2.在registeredSingletons中添加“yadang”。 3.在earlySingletonObjects中移除key为“yadang”的对象(不存在也不会报错)。
到目前为止,bean工厂中各个缓存容器的值如下:
singletonObjects | null |
registeredSingletons |
"yadang" |
singletonFactories |
<yadang, factory> |
earlySingletonObjects |
null |
singletonsCurrentlyInCreation |
"yadang" |
亚当创建完成后,就需要通过populateBean对属性进行依赖注入,流程会陷入inject方法,最终通过doResolveDependency一路调用到getBean(“xiawa”)。这时流程就走到了夏娃的创建流程(粉色流程)。
2.2 夏娃的诞生
由于bean工厂中singletonObjects并未缓存有夏娃这个bean(beanName是“xiawa”)。那么夏娃的创建流程就和亚当的类似,它也会通过doCreateBean方法中createBeanInstance创建出夏娃eveObj这个对象
[code]eve: adam : null
然后也通过addSingletonFactory,如果在singletonObjects中没有找到“xiawa”这个bean,那么就尝试在bean工厂的singletonFactories中进行如下步骤:
[code]1.添加<xiawa, singletonFactory>到singletonFactories中。 2.在registeredSingletons中添加“xiawa”。 3.在earlySingletonObjects中移除key为“xiawa”的对象(不存在也不会报错)。
到目前为止,bean工厂中各个缓存容器的值如下:
singletonObjects | null |
registeredSingletons |
"yadang", "xiawa" |
singletonFactories |
<yadang, factory>, <xiawa, factory> |
earlySingletonObjects |
null |
singletonsCurrentlyInCreation |
"yadang", "xiawa" |
同样的,夏娃创建完成后,就需要通过populateBean对属性进行依赖注入,流程会陷入inject方法,最终通过doResolveDependency一路调用到getBean(“yadang”)。这时流程又就走到了亚当的创建流程(蓝色流程)。
2.3 寻找亚当
再次进入蓝色的getBean-->doGetBean-->getSingleton("yadang")-->getSingleton("yadang",true)。
[code]protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 当前singletonObjects中不存在yadang和xiawa对象,故singletonObject=null Object singletonObject = this.singletonObjects.get(beanName); // "yadang", "xiawa", 显然存在"yadang" if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // earlySingletonObjects中不存在"yadang", "xiawa",传入的allowEarlyReference为true singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 通过工厂获取到singletonFactory ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); 1c6f4 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
根据bean工厂中各个缓存容器的值层层过滤后,最终满足所有条件。进入到最内层if语句块,执行方法singletonObject = singletonFactory.getObject();该方法是DefaultSingletonBeanRegistry中addSingletonFactory方法的回调函数() -> getEarlyBeanReference(beanName, mbd, bean)。它最终会执行AbstractAutowireCapableBeanFactory的getEarlyBeanReference方法后得到缓存的bean。有可能你会好奇入参是怎么来的,根据“yadang”拿到的singletonFactory 中有四个缓存值如下:1是DefaultListableBeanFactory,2,3,4就是入参。因为DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory,故父类的getEarlyBeanReference方法可以调用。
[code]singletonFactory = {AbstractAutowireCapableBeanFactory$lambda@1915} arg$1 = {DefaultListableBeanFactory@1387} "org.springframework.beans.factory.support.DefaultListableBeanFactory@36bc55de: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,caseApplication,yadang,xiawa,human,appleFactory,gardenofEden]; root of factory hierarchy" arg$2 = "yadang" arg$3 = {RootBeanDefinition@1865} "Root bean: class [com.Hodey.learn.bean.Adam]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [F:\gitSource\HodeyLab\case\target\classes\com\Hodey\learn\bean\Adam.class]" arg$4 = {Adam@1867}
最终经过上述步骤返回出入参4对象,如果使用了动态代理,那么返回代理对象。在此之前还要对earlySingletonObjects和singletonFactories做相应的操作。此时bean工厂中各个缓存容器的值如下:
singletonObjects | null |
registeredSingletons |
"yadang", "xiawa" |
singletonFactories |
<xiawa, factory> |
earlySingletonObjects |
<"yadang", adam> |
singletonsCurrentlyInCreation |
"yadang", "xiawa" |
拿到bean后还需要进入getObjectForBeanInstance分支处理万一拿到的bean是FactoryBean的情况:
[code]protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // 首先进行是否是FactoryBean推断,如果beanInstance是null,则返回null;如果 if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } // 如果beanInstance和name关于是否FactoryBean自相矛盾的异常抛出 if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } } // 普通bean直接返回beanInstance if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } // 如果是FactoryBean,先看看有没有想要得到的目标对象在之前有没有造出来。如果没有就通过FactoryBean造一个。 Object object = null; if (mbd == null) { object = getCachedObjectForFactoryBean(beanName); } if (object == null) { FactoryBean<?> factory = (FactoryBean<?>) beanInstance; if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); // 让FactoryBean造一个beanName的对象 // 内部调用doGetObjectFromFactoryBean(factory, beanName)#factory.getObject()方法的干活。factory是FactoryBean的实现类。 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
最终通过几步简单的调用步骤后,返回最终获取到的adam对象,红色doResolveDependency方法的getBean方法执行完成执行后一路返回,最终在inject中完成夏娃对象adam属性的装配,这是eveObj对象:
[code]eve: adam : adamObj
完成eve对象的装配后一路返回doGetBean方法。随后通过afterSingletonCreation方法移除singletonsCurrentlyInCreation中的中的“xiawa”,通过getSingleton(String beanName, ObjectFactory<?> singletonFactory)向容器中的做如下操作:
[code]1. singletonObjects中添加<"xiawa", eveObj> 2. singletonFactories中移除<"xiawa", factoryObj> 3. earlySingletonObjects中移除<"xiawa", eveObj> 4. registeredSingletons中添加"xiawa"
这样bean工厂中各个缓存容器的值如下:
singletonObjects | <"xiawa", eveObj> |
registeredSingletons |
"yadang", "xiawa" |
singletonFactories |
null |
earlySingletonObjects |
<"yadang", adam> |
singletonsCurrentlyInCreation |
"yadang" |
这样,夏娃的创建过程就完毕了。流程一路返回eveObj对象到蓝色的doResolveDependency方法。
2.4 完成亚当的创建
一路返回eveObj对象到inject完成adamObj的装配:
[code]adam: eve : eveObj
随后一路返回到getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法的afterSingletonCreation和addSingleton方法操作bean工厂中各个缓存容器后的值如下:
singletonObjects | <"xiawa", eveObj>, <"yadang", adamObj> |
registeredSingletons |
"yadang", "xiawa" |
singletonFactories |
null |
earlySingletonObjects |
null |
singletonsCurrentlyInCreation |
null |
此时亚当和夏娃这两个bean都缓存在了singletonObjects中,完成了这两个循环依赖bean的创建过程。
循环依赖仅仅是Spring创建bean过程中的一个小小的插曲,时间还停留在原来的6:00。下篇文章我们将会深入分析本系列文章最后一个内容——initializeBean。敬请期待。
3. 附录:本项目工程文件
[code]@Component("yadang") public class Adam { @Autowired private Eve eve; public void sayHello(){ System.out.println("你好,我是" + this.getClass() + ". 我爱" + eve.getClass()); } }
[code]@Component("xiawa") public class Eve { @Autowired private Adam adam; public void sayHello(){ System.out.println("你好,我是" + this.getClass() + ". 我爱" + adam.getClass()); } }
[code]@Service public class GardenofEden { @Autowired private Adam adam; @Resource(name = "xiawa") private Eve eve; public void sayHello(){ adam.sayHello(); eve.sayHello(); } }
[code]@ComponentScan("com.Hodey") public class CaseApplication { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(CaseApplication.class); GardenofEden garden = ctx.getBean(GardenofEden.class); garden.sayHello(); } }
执行结果:
[code]你好,我是class com.Hodey.learn.bean.Adam. 我爱class com.Hodey.learn.bean.Eve 你好,我是class com.Hodey.learn.bean.Eve. 我爱class com.Hodey.learn.bean.Adam
- 从spring源码角度分析循环依赖bean的组装
- 详解Spring-bean的循环依赖以及解决方式
- Spring源码-IOC容器(六)-bean的循环依赖
- Spring源码阅读-BeanFactory-循环依赖
- Spring源码学习--Bean对象循环依赖问题解决(四)
- Spring IOC 容器源码分析 - 循环依赖的解决办法
- spring 源码探索--单例bean解决循环依赖问题
- Spring循环依赖正确性及Bean注入的顺序关系详解
- Spring IOC/BeanFactory/ApplicationContext的工作流程/实现原理/初始化/依赖注入源码详解
- Spring-bean的循环依赖以及解决方式
- Spring IOC源码详解之容器初始化
- Spring源码解析:Bean的实例化与依赖注入(四)
- 详解Spring循环依赖的解决方案
- Spring源码解析:循环依赖的探测与处理
- Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
- Spring IOC 源码分析-bean标签解析
- spring的BeanFactory和ApplicationContext源码详解(一)