妳需要我,我需要妳就是循環依賴
在Spring中使用的三級緩存來解決循環依賴問題,這裏的緩存其實就是Map對象
當獲取壹個Bean時會先從緩存中查找是否有相應的Bean。
1 創建A實例
2 將A實例(半初始化,屬性沒有填充)暴露放入緩存中
3 填充A實例的屬性
4 A實例屬性依賴B對象
5 創建B對象實例
6 填充B實例屬性
7 B實例屬性依賴A對象
8 將上面已經暴露到三級緩存中的A對象註入給B實例
在獲取A對象的時候執行上面27.1中的getSingleton方法,會將三級緩存中A這個半初始化狀態的對象移除,將其存入到二級緩存中。
9 B實例Bean的創建工作繼續執行初始化方法
B如果需要AOP代理?最終B對象是個代理對象。B到此就完全的初始化完了,B的依賴對象A此時是個半初始化狀態的對象
10 B實例對象保存到壹級緩存
最終B實例創建,初始化都執行完後會將自身加入到壹級緩存同時清除二級,三級緩存
11 A實例Bean創建繼續執行
如果B是被AOP代理的那麽此時的A實例註入的B對象就是壹個代理對象。
12 A實例Bean執行初始化方法
13 A繼續執行上面的10步驟
三級緩存解決問題:循環依賴+AOP問題
只用壹,二級緩存:
從上面羅列的步驟看似乎很是完美解決了循環依賴問題,接下來我們看看加入AOP的場景
假如A,B兩個對象最終都是要被AOP代理的
執行到這裏,A中依賴的B是代理對象沒有問題,但是B中依賴的A對象是原始對象;這就不正確了應該依賴的A也必須是代理對象才是。
引入三級緩存:
三級緩存引入了ObjectFactory對象,在獲取對象的時候,是調用ObjectFactory#getObject方法。
而這個getObject方法的實現實際執行的是getEarlyBeanReference方法,再來回顧下:
在創建實例時先將其存入三級緩存中:
getEarlyBeanReference方法就是提前創建代理對象
如果開啟了AOP代理後
通過getEarlyBeanReference方法提前創建代理對象。這樣就解決了循環依賴時AOP代理問題。保證獲取的都是同壹個對象。
其實引入三級緩存還解決了壹個問題就是延遲代理對象的創建,如果不應用ObjectFactory方式那麽我們需要不管需不需要都要先創建代理對象,而引入ObjectFactory可以在註入的時候先暴露的是ObjectFactory只有在調用getObject方法的時候才去創建真正的代理對象(避免了所有Bean都強制創建代理對象)。當沒有被代理時可以直接返回原始對象,如果被代理會提前創建代理對象。
不用二級直接是用壹,三級緩存?
假設場景:A 依賴 B,B 依賴 A、C,C 依賴 A
如果這樣會出現不同的代理對象,每次調用getObject都會創建不同的代理對象(在上面的場景中如果只用壹,三級緩存那麽 B 依賴 A會通過getObject獲取壹個代理對象Proxy$1,接著註入C的時候 C中又依賴A,那這時候又從getObject獲取對象那麽返回的將又會是壹個新的代理對象Proxy$2;在這個過程中A對象就出現了2個不壹樣的對象了,這肯定是錯誤的)。而引入二級緩存也就解決了這個問題。只有二級緩存沒有的時候才從三級緩存匯總獲取(如果需要則創建代理對象,然後保存到二級緩存中,二級緩存中已經是提前創建了代理對象(如果需要代理))。
當壹個Bean完全的創建完以後放入壹級緩存中,此時會吧二級三級中的緩存清除。
完畢!!!!
SpringMVC參數統壹驗證方法
SpringBoot多數據源配置詳解
SpringCloud Nacos 整合feign
Spring AOP動態代理失效的解決方法@Transactional為何會失效
SpringBoot配置文件妳了解多少?
SpringBoot郵件發送示例 Springboot面試題整理附答案
SpringBoot項目查看線上日誌
在Spring Cloud 中妳還在使用Ribbon快來試試Load-Balancer
SpringBoot分庫分表sharding-sphere3