Spring迴圈依賴
?生命不息,寫作不止
? 繼續踏上學習之路,學之分享筆記
? 總有一天我也能像各位大佬一樣
? 一個有夢有戲的人 @怒放吧德德
?分享學習心得,歡迎指正,大家一起學習成長!
什麼是迴圈依賴?
什麼是迴圈依賴呢?簡單來說就是beanA依賴於beanB,beanB依賴於beanA(也就是A類中使用了B類,B類使用了A類)。在bean建立的生命週期中,當建立了beanA的時候,會檢索A物件內部中需要填充的物件,發現A中是需要用到B物件,會先去單例池中尋找,如果沒有找到,就會去建立B的bean物件。在beanB的生命週期中,建立方式依然是相同的,因此也會去填充beanA,發現單例池中並沒有A的bean物件,這樣就造成了迴圈依賴。
我們可以看看兩個相互依賴的bean的生命週期
如何解決迴圈依賴
兩個bean物件在還沒有建立成bean物件就由於相互的依賴而進入的迴圈,那麼,要如何去解開他們的迴圈呢?在spring中會使用三級快取。在springboot中有許多的解決方法,比如加配置,加註解等等操作。
打破迴圈依賴
想要打破迴圈依賴,就只需要一個快取即可,也就是使用一個Map集合。簡單的說就是將所需的物件存入裡面,在檢測單例池沒有所需的bean物件的時候,就透過beanName去查詢一級快取,這裡要特別注意的是,一級快取此時儲存的是普通物件,是透過構造方法例項化的物件。
使用map來儲存物件,可見能夠解除迴圈依賴,但是這裡儲存的是普通物件,而不是一個代理物件。那麼要如何解決呢?
提前AOP
可以透過提前AOP來得到代理物件,再把代理物件存到map集合中。在例項化普通物件後,直接進行AOP,生成代理物件,再將代理物件存到map集合中,然後在後面也就不需要進行aop了。但是,並不是每次都將AOP提前的,是在發現了迴圈依賴才是需要去將AOP提前。
判斷是否迴圈依賴
要判斷是否迴圈依賴也不是很難,就只需要一個集合creatingSet<'beanName'>。在例項化物件之前將這個bean的名字存到set集合中,表示這個bean物件正在建立中,等待依賴他的物件例項化後,會去判斷set集合中是否有所依賴的beanName,如果有的話,就表示出現了迴圈依賴,這樣就可以進行提前AOP。然而,在bean物件建立完畢並且新增到單例池之後,就要去吧set集合把這個beanName移除掉,不然就會造成每次都需要提前AOP。
一級快取
一級快取singletonObjects存放的是已經初始化好的bean,即已經完成初始化好的注入物件的代理
二級快取
透過提前AOP獲得的代理物件,是不能夠直接加入到單例池中的。因為生成的代理物件是需要利用到普通物件的,而這時候的普通物件是不完整的(沒有透過完整的生命週期),裡面的屬性可能是為空的。也會造成這個代理物件不是單例的。我們可以採用二級快取earlySingletonObjects,將提前AOP生成的代理存放到earlySingletonObjects集合中。
在填充屬性的時候,先到單例池尋找,沒找到就去creatingSet看是否存在迴圈依賴,存在迴圈依賴之後就去earlySingletonObjects尋找是否有代理物件,沒有再去提前AOP建立代理物件,並存入快取中。在最後填充其他屬性之後從二級快取中去get代理物件,存入單例池中。
三級快取
打破迴圈並不是只透過二級快取,而是還需要一個三級快取singletonFactory。addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
spring在建立bean的時候就會生成lambda表示式,存到三級快取中。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
而執行lambda表示式就會去執行wrapIfNecessary這個方法
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
這個方法就能夠去建立代理物件
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
三級快取主要才是用來解決迴圈依賴。在例項化物件的時候就將lambda表示式存入三級快取中,singletonFActories.put("beanName", addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)));
這個表示式的bean實際上就是普通物件,最後會生成的是代理物件。當遇到迴圈依賴的時候,就會先去二級快取找再去三級快取找,並且一定會找到,找到的是lambda表示式,然後去執行lambda表示式去生成代理物件最後當道二級快取中。
博文推薦
Spring 迴圈依賴及三級快取_程式源程式的部落格-CSDN部落格_三級快取
?創作不易,如有錯誤請指正,感謝觀看!記得點贊哦!?