一篇文章帶你理解Java Spring三級緩存和循環(huán)依賴
一、什么是循環(huán)依賴?什么是三級緩存
【什么是循環(huán)依賴】什么是循環(huán)依賴很好理解,當(dāng)我們代碼中出現(xiàn),形如BeanA類中依賴注入BeanB類,BeanB類依賴注入A類時,在IOC過程中creaBean實(shí)例化A之后,發(fā)現(xiàn)并不能直接initbeanA對象,需要注入B對象,發(fā)現(xiàn)對象池里還沒有B對象。通過構(gòu)建函數(shù)創(chuàng)建B對象的實(shí)例化。又因B對象需要注入A對象,發(fā)現(xiàn)對象池里還沒有A對象,就會套娃。
【三級緩存】三級緩存實(shí)際上就是三個Map對象,從存放對象的順序開始
三級緩存singletonFactories存放ObjectFactory,傳入的是匿名內(nèi)部類,ObjectFactory.getObject() 方法最終會調(diào)用getEarlyBeanReference()進(jìn)行處理,返回創(chuàng)建bean實(shí)例化的lambda表達(dá)式。
二級緩存earlySingletonObjects存放bean,保存半成品bean實(shí)例,當(dāng)對象需要被AOP切面代時,保存代理bean的實(shí)例beanProxy
一級緩存(單例池)singletonObjects存放完整的bean實(shí)例
/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

二、三級緩存如何解決循環(huán)依賴?
【如何解決循環(huán)依賴】Spring解決循環(huán)依賴的核心思想在于提前曝光,首先創(chuàng)建實(shí)例化A,并在三級緩存singletonFactories中保存實(shí)例化A的lambda表達(dá)式以便獲取A實(shí)例,當(dāng)我沒有循環(huán)依賴和AOP時,這個三級緩存singletonFactories是沒用在后續(xù)用到的。
但是當(dāng)我A對象需要注入B對象,發(fā)現(xiàn)緩存里還沒有B對象,創(chuàng)建B對象并又上述所說添加進(jìn)三級緩存singletonFactories,B對象需要注入A對象,這時從半成品緩存里取到半成品對象A,通過緩存的lambda表達(dá)式創(chuàng)建A實(shí)例對象,并放到二級緩存earlySingletonObjects中。
此時B對象可以注入A對象實(shí)例和初始化自己,之后將完成品B對象放入完成品緩存singletonObjects。但是當(dāng)有aop時,B對象還沒有把完成品B對象放入完成品緩存singletonObjects中,B對象初始化后需要進(jìn)行代理對象的創(chuàng)建,此時需要從singletonFactories獲取bean實(shí)例對象,進(jìn)行createProxy創(chuàng)建代理類操作,這是會把proxy&B放入二級緩存earlySingletonObjects中。這時候才會把完整的B對象放入完成品一級緩存也叫單例池singletonObjects中,返回給A對象。
A對象繼續(xù)注入其他屬性和初始化,之后將完成品A對象放入完成品緩存。

三、使用二級緩存能不能解決循環(huán)依賴?
一定是不行,我們只保留二級緩存有兩個可能性保留一二singletonObjects和earlySingletonObjects,或者一三singletonObjects和singletonFactories
【只保留一二singletonObjects和earlySingletonObjects】
流程可以這樣走:實(shí)例化A ->將半成品的A放入earlySingletonObjects中 ->填充A的屬性時發(fā)現(xiàn)取不到B->實(shí)例化B->將半成品的B放入earlySingletonObjects中->從earlySingletonObjects中取出A填充B的屬性->將成品B放入singletonObjects,并從earlySingletonObjects中刪除B->將B填充到A的屬性中->將成品A放入singletonObjects并刪除earlySingletonObjects。
這樣的流程是線程安全的,不過如果A上加個切面(AOP),這種做法就沒法滿足需求了,因?yàn)?code>earlySingletonObjects中存放的都是原始對象,而我們需要注入的其實(shí)是A的代理對象。
【只保留一三singletonObjects和singletonFactories】
流程是這樣的:實(shí)例化A ->創(chuàng)建A的對象工廠并放入singletonFactories中 ->填充A的屬性時發(fā)現(xiàn)取不到B->實(shí)例化B->創(chuàng)建B的對象工廠并放入singletonFactories中->從singletonFactories中獲取A的對象工廠并獲取A填充到B中->將成品B放入singletonObjects,并從singletonFactories中刪除B的對象工廠->將B填充到A的屬性中->將成品A放入singletonObjects并刪除A的對象工廠。
同樣,這樣的流程也適用于普通的IOC已經(jīng)有并發(fā)的場景,但如果A上加個切面(AOP)的話,這種情況也無法滿足需求。
因?yàn)槟玫絆bjectFactory對象后,調(diào)用ObjectFactory.getObject()方法最終會調(diào)用getEarlyBeanReference()方法,getEarlyBeanReference這個方法每次從三級緩存中拿到singleFactory對象,執(zhí)行g(shù)etObject()方法又會產(chǎn)生新的代理對象
所有這里我們要借助二級緩存來解決這個問題,將執(zhí)行了singleFactory.getObject()產(chǎn)生的對象放到二級緩存中去,后面去二級緩存中拿,沒必要再執(zhí)行一遍singletonFactory.getObject()方法再產(chǎn)生一個新的代理對象,保證始終只有一個代理對象。
getSingleton()、getEarlyBeanReference() 源碼如下
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); // 先從一級緩存拿
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName); // 拿二級緩存
if (singletonObject == null && allowEarlyReference) {
// 拿三級緩存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 最終會調(diào)用傳入的匿名內(nèi)部類getEarlyBeanReference()方法,這里面沒調(diào)用一次會生成一個新的代理對象
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java NIO實(shí)現(xiàn)群聊系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java NIO實(shí)現(xiàn)群聊系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
Java設(shè)計模式編程中的責(zé)任鏈模式使用示例
這篇文章主要介紹了Java設(shè)計模式編程中的責(zé)任鏈模式使用示例,責(zé)任鏈模式可以避免很多請求的發(fā)送者和接收者之間的耦合關(guān)系,需要的朋友可以參考下2016-05-05
SpringBoot集成ActiveMQ的實(shí)戰(zhàn)全過程
消息隊(duì)列中間件是分布式系統(tǒng)中重要的組件,主要解決應(yīng)用耦合、異步消息、流量削鋒等問題,實(shí)現(xiàn)高性能、高可用、可伸縮和最終一致性架構(gòu),是大型分布式系統(tǒng)不可缺少的中間件,這篇文章主要給大家介紹了關(guān)于SpringBoot集成ActiveMQ的相關(guān)資料,需要的朋友可以參考下2021-11-11
Java?spring?通過注解方式創(chuàng)建對象的示例詳解
這篇文章主要介紹了java?spring?通過注解方式創(chuàng)建對象,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02
Java基礎(chǔ)字符編碼與內(nèi)存流詳細(xì)解讀
這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08
Set接口深入剖析之HashSet、LinkedHashSet和TreeSet
這篇文章主要介紹了Set接口深入剖析之HashSet、LinkedHashSet和TreeSet,LinkedHashSet是HashSet的子類,實(shí)現(xiàn)了Set接口,LinkedHashSet底層是一個LinkedHashMap,底層維護(hù)了一個數(shù)組+雙向鏈表,需要的朋友可以參考下2023-09-09

