什么是循环依赖
现有两个对象A,B,在对象A维护了属性B,在对象B维护了属性A,在创建A对象时给属性B赋值,那要给B赋值首先要创建B对象,创建B对象又要给A赋值,两者就构成了循环依赖。Spring是如何解决的呢?
Spring是如何解决循环依赖的
Spring解决循环依赖的前提是:两个对象必须是单例对象,并且属性是基于set方法注入。如果两个对象不是单例对象,不管是否使用set方法注入,都不能解决循环依赖的问题。如果两个对象是多实例的,即使使用set方法,也不能解决循环依赖问题。
从创建对象看起,现在要创建一个对象A,首先是使用getBean方法进行A对象的获取,从单例池(一级缓存)中去调用,即使用getBean内部的getSingleton()获取对象A,但是不能获取对象A,因为还没创建。那么需要使用doCeateBaen()进行创建A,它的底层首先实例化对象:反射的机制创建A,此时A里面的属性B不存在,即B为空,之后进行属性的填充:这里先获取对象B,调用getSingleton(),由于B是第一次创建,不能获取B,这时候需要实例化B,由于B为空,所以要进行属性的填充,要进行填充A属性,那么首先要获取A。此时,能否获取对象A,如何可以,从哪里获取,如果不能获取对象A,这是怎么办,又有什么问题。最后进行初始化,这是才是一个完全可用的对象。看乱了吗。看下图:
如果获取不到,只能够去创建,但是创建就会导致两个对象A,这不是单例对象了。因此只能怪去获取。那么从哪里获取对象?不能从单例池(一级缓存)中获取,因为此时对象不是一个完全体对象。
如果一个对象不是完全体的,我们应该也要将其存储,但不是存储在单例池中。单例池是一个什么样的数据结构?在Bean的创建时,在AbstractBeanFactory中有个doGetBean方法,里面调用的getSingleton,在这个方法中,首先通过this.singletonObject(一级缓存)中获取bean,获取不到从this.earlySingletonObject(二级缓存)中获取bean,当然,最后也可以从this.singleton-Factories()获取bean,这些缓存在DefaultSigntonBeanRegistry中定义
其中singletonObject保存的时完整的对象(实例化,属性填充,初始化)。那么非完全体对象存储在哪里?在AbstractAutoWireCapableBeanFactory这个类中的doCreateBean方法中
这里创建的是不完全对象,在下面这里才是完全的初始化,即initalizeBean中,那么在554到991之间是做了不完全体对象的存储。
就是在这里
可以得出 ,它把lambda表达式存到三级缓存中,存的是一个方法。在addSingletonFactory里面存储不完全体的a,存储的是一个方法,这个方法,其实可以都当前这个对象进行代理对象的产生。
获取不完全体对象,其实就是从singleFactories中进行获取
为什么这里会有代理对象?假设现在A有代理对象,对A做了增强,B也有代理对象,对B进行了增强。那么属性填充时获取到的对象也是代理对象,那么此时的这个代理对象从哪里获取?也就是说现在对象还没有创建,如何进行获取到增强的对象?但是此时又需要获取代理对象,怎么办?那么只能在对象创建时但是还没有来得急属性赋值时进行操作,就是在下图这里进行代理对象的产生,即lambda表达式就是在创建(产生)代理对象,lambda表达式是在调用addSingletonFactory这个方法调用时开始执行。当然,代理对象产生之后也是需要存储的,存储在代理缓存池中。
那么,获取代理对象就是从代理缓存池中获取,获取之后,进行属性填充,此时的A是一个完全体对象了,它的属性B是一个代理的,之后A进行初始化,由于代理缓存池中存储了代理对象,代理对象此时就需要从代理缓存池中获取
总结
循环依赖时两个对象相互引用,要解决循环依赖,两个对象必须是单例对象,并且属性是基于set方法注入。如果率先创建A对象,首先尝试通过getBean里的getSingleton获取对象A,由于对象A第一次创建,所以这里获取不到,所以这里只能调用doCreateBean方法进行对象A的创建,基于反射机制。创建完成的A是一个非完全体对象(只是进行创建,没有经历属性的注入以及初始化),接下来进行属性填充,由于属性B是一个对象,只能够进行获取,但是调用getBean的getSingleton并不能获取到B对象,因此此时只能够创建对象B,此时的B也是非完全体对象,此时要进行属性的填充,需要获取对象A,但是这是可以获取到,因为获取不到的话只能够将那些创建,创建导致A不是了单例对象。可以从单例池中吗?因为对象创建完成后都是存放在单例池中,但是单例池只能存储完全体的对象,此时的A不是完全体对象。那么就需要从存储非完全体对象的地方去获取,通过addSingletonFactory这个方法可知存入到了三级缓存中,存的是beanName和singletonFactory,singletonFactory是一个函数式接口,这个接口里有个getObject方法。只要调用函数式接口的getObject这个方法,才会回调lambda表达式中的getEarlyBeanReference这个方法,getEarlyBeanReference()做了对象代理的产生,为了解决对象A,B都有代理对象,那么这时代理对象就在getEarlyBeanReference()产生的,此时就获取到了非完全体对象A,此时就可以对对象B进行属性的填充,之后对象B进行初始化(完整的B),B有了之后,那么A这个属性就可以进行填充,A进行初始化。如果A有代理对象的话,那么也是需要增强,由于前面产生的代理对象存放在代理缓存池中,A直接从代理缓存池中获取即可。
再细化些
在前面提到,getBean中的getSingleton获取对象,那它是如何进行获取的,我们来看看getSingleton这个方法,在java/org/springframework/beans/factory/support路径下的DefaultSingletonBeanRegistry.java的类中,有getSingleton这个方法,首先是从一级缓存中获取,一级为空的话再从二级缓存中获取,二级为空再从三级缓存中获取,为了更好的演示,为每行代码进行标识主要代码如下:
Object singletonObject = this.singletonObjects.get(beanName); // 1
singletonObject = this.earlySingletonObjects.get(beanName); // 2
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); // 3
if (singletonFactory != null) { // 4
singletonObject = singletonFactory.getObject(); // 5
this.earlySingletonObjects.put(beanName, singletonObject); // 6
this.singletonFactories.remove(beanName); // 7
}
接下来分析下三次进入getSingleton()方法分别执行那些行代码。
第一次,由于不存在A对象,执行1,2,3行代码均为null,进不到if里面,第二次,由于不存在B对象,同理。第三次是获得的A是一个非完全体对象,在三级缓存中通过lambda表达式进行存储,此时进入第四行代码,第五行调用getObject,这个getObject回调lambda表达式,lambda表达式执行返回对象A,之后执行第六行代码,在earlySingletonObject(二级缓存)中进行存储,之后执行第7行代码移除存储在三级缓存中的对象A,A拿到后给B进行赋值,B完成对象初始化,此时整个B是一个完整对象,此时B被放在一级缓存,那么给A进行赋值操作,A完成初始化,A被放在一级缓存。最后把二级缓存中的A删除