深度讲解spring的循环依赖以及三级缓存
如下例子
A.class
B.class
@Component("b") public class B {@Autowiredprivate A a; }主程序
@ComponentScan("com.gzq.dependencytest") public class DependencytestApplication {public static void main(String[] args) {AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();annotationConfigApplicationContext.getBean("a");} }這樣會順利的得到bean對象
在創(chuàng)建a時首先spring找到a的class,隨后操作beandefinition,并推斷構(gòu)造方法->實例化->對象->屬性填充->afterPropertiesSet()->AOP(如果沒有aop就不會進(jìn)行該操作,也不會生成代理對象),判斷是否有切面表達(dá)式操作此類,如果有會創(chuàng)建出a的代理對象,代理對象里的target指向里面真正的屬性,最后spring完成創(chuàng)建,把對象放入單例池
在此案例中,a對象在創(chuàng)建的時候會先去單例池找b,但是此時單例池中并沒有b對象,這時就會去創(chuàng)建b,但是b對象里也需要a對象屬性,這時,spring會去單例池中找a對象,但是a此時還在構(gòu)造中,還沒有創(chuàng)建完成,這樣就造成了循環(huán)依賴,到底spring是如何解決循環(huán)依賴的呢。
解答:運用了三級緩存,也就是三個map,在新版本中,兩個concurrent的map,剩下一個都是普通的map,是二級緩存。
在a對象剛剛初始化時(還未完成),會把new出來的a,此時a里面還沒有賦值,此時spring就會把a(bǔ)放入一個map中,map的key就是a class的名字,value就是剛創(chuàng)造出來的原始a對象,其實這里放入的是一個lambda表達(dá)式,里面除了有a的原始對象,還有bean的定義beandefinition,beanname,這里的三級緩存也不一定用得上,得看a到底有沒有進(jìn)行aop,同樣b也是同樣的方法把原始b對象和一些bean定義放入一個map中。
這時候,可以解決上面b在創(chuàng)建時找a沒有在單例池找到的問題,它會在剛才創(chuàng)造的map中找到一個原始a的對象(注意這里是原始的a對象,這時候a對象的里面的屬性是空的,這看上去是一個問題,其實這個問題不是一個問題,因為最終這個a中的對象b是會被賦值的,只是把a(bǔ)中的b對象賦值的這個動作放在了后面【對象引用】),接下來b對象會把剩余的動作做完,把b對象放入單例池中,這樣,a就可以在單例池中找到b。【注:這里的單例池也就是一個concurrentmap,屬于一級緩存,原始a,b對象放入的map是三級map】
這里會引入一個問題,就是aop,因為一旦a對象里的某個方法被aop切到,這時候放在一級map(單例池SingletonObjects)中的對象是代理對象(cglib),a的代理對象和a的原始對象并不是一個對象,a的代理對象里的target屬性指向了a的原始對象,這樣就會引出問題,在b對象找到a對象給a對象賦值時應(yīng)該找到這個代理a對象,但是aop操作在前面講到是在創(chuàng)建完對象后才會進(jìn)行的,可是現(xiàn)在的a對象還沒有到達(dá)這一步,我們只能提前進(jìn)行aop,只有在某個特殊的情況下需要進(jìn)行提前aop,這個特殊條件就是當(dāng)a出現(xiàn)了循環(huán)依賴。如何檢測a出現(xiàn)了循環(huán)依賴呢?
這時候會有一個createingSet(),在創(chuàng)建a之前,就會把該行為記錄到這個set中,當(dāng)a創(chuàng)建完畢后放入一級緩存中時就會移出該值,這時候在上述的b找a時,發(fā)現(xiàn)單例池中沒有找到a對象,這時候就會去creatingSet中找是否有a對象,這時候就判斷出了a和b存在循環(huán)依賴,這時候就會提前進(jìn)行aop,得到a的代理對象,這時候就可以把創(chuàng)建出來的a的代理對象給b中的a賦值,這時候要注意在得到a對象的代理對象時,不能把它放到單例池中,因為這時候的a的代理對象并沒有完全的進(jìn)行完bean的生命周期(不完整的a代理對象),這時候再想一個問題,如果a對象里還有一個c對象屬性,c中也調(diào)用了a,那么經(jīng)歷了和b一樣的過程中,這時候spring又會創(chuàng)建出一個a代理對象,這就不合理了,應(yīng)該b和c應(yīng)該是同一個a的代理對象,那么這樣就又需要一個map(earlySingletonObjects 二級緩存),這里的map也是key是beanname,value是object,所以這時候要擴(kuò)充上面的說法,在判斷出是循環(huán)依賴后,先會去earlySingletonObjects中找是否有,如果沒有再創(chuàng)建a的代理對象,在完成所有的其他事情以后,這時候earlySingletonObjects中的=可以取到a的代理對象,此時已經(jīng)完成了a代理對象的創(chuàng)建,把它再放入到代理池中。
這時候再強(qiáng)調(diào)三級緩存的作用,因為a代理對象里的target指向的是a的原始對象,這時候之前的map已經(jīng)保存了,這就用上了三級緩存。
分析源碼:
以上純屬個人見解,希望大家多多指正。
總結(jié)
以上是生活随笔為你收集整理的深度讲解spring的循环依赖以及三级缓存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spark从hbase读数据到存入hba
- 下一篇: spring无法用三级缓存解决循环依赖的