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