關於Java Spring三級緩存和循環依賴的深入理解
一、什麼是循環依賴?什麼是三級緩存?
【什麼是循環依賴】什麼是循環依賴很好理解,當我們代碼中出現,形如BeanA類中依賴註入BeanB類,BeanB類依賴註入A類時,在IOC過程中creaBean實例化A之後,發現並不能直接initbeanA對象,需要註入B對象,發現對象池裡還沒有B對象。通過構建函數創建B對象的實例化。又因B對象需要註入A對象,發現對象池裡還沒有A對象,就會套娃。
【三級緩存】三級緩存實際上就是三個Map對象,從存放對象的順序開始
三級緩存singletonFactories存放ObjectFactory,傳入的是匿名內部類,ObjectFactory.getObject() 方法最終會調用getEarlyBeanReference()進行處理,返回創建bean實例化的lambda表達式。
二級緩存earlySingletonObjects存放bean,保存半成品bean實例,當對象需要被AOP切面代時,保存代理bean的實例beanProxy
一級緩存(單例池)singletonObjects存放完整的bean實例
/** 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);
二、三級緩存如何解決循環依賴?
【如何解決循環依賴】Spring解決循環依賴的核心思想在於提前曝光,首先創建實例化A,並在三級緩存singletonFactories中保存實例化A的lambda表達式以便獲取A實例,當我沒有循環依賴和AOP時,這個三級緩存singletonFactories是沒用在後續用到的。
但是當我A對象需要註入B對象,發現緩存裡還沒有B對象,創建B對象並又上述所說添加進三級緩存singletonFactories,B對象需要註入A對象,這時從半成品緩存裡取到半成品對象A,通過緩存的lambda表達式創建A實例對象,並放到二級緩存earlySingletonObjects中。
此時B對象可以註入A對象實例和初始化自己,之後將完成品B對象放入完成品緩存singletonObjects。但是當有aop時,B對象還沒有把完成品B對象放入完成品緩存singletonObjects中,B對象初始化後需要進行代理對象的創建,此時需要從singletonFactories獲取bean實例對象,進行createProxy創建代理類操作,這是會把proxy&B放入二級緩存earlySingletonObjects中。這時候才會把完整的B對象放入完成品一級緩存也叫單例池singletonObjects中,返回給A對象。
A對象繼續註入其他屬性和初始化,之後將完成品A對象放入完成品緩存。
三、使用二級緩存能不能解決循環依賴?
一定是不行,我們隻保留二級緩存有兩個可能性保留一二singletonObjects和earlySingletonObjects,或者一三singletonObjects和singletonFactories
【隻保留一二singletonObjects和earlySingletonObjects】
流程可以這樣走:實例化A ->將半成品的A放入earlySingletonObjects中 ->填充A的屬性時發現取不到B->實例化B->將半成品的B放入earlySingletonObjects中->從earlySingletonObjects中取出A填充B的屬性->將成品B放入singletonObjects,並從earlySingletonObjects中刪除B->將B填充到A的屬性中->將成品A放入singletonObjects並刪除earlySingletonObjects。
這樣的流程是線程安全的,不過如果A上加個切面(AOP),這種做法就沒法滿足需求瞭,因為earlySingletonObjects中存放的都是原始對象,而我們需要註入的其實是A的代理對象。
【隻保留一三singletonObjects和singletonFactories】
流程是這樣的:實例化A ->創建A的對象工廠並放入singletonFactories中 ->填充A的屬性時發現取不到B->實例化B->創建B的對象工廠並放入singletonFactories中->從singletonFactories中獲取A的對象工廠並獲取A填充到B中->將成品B放入singletonObjects,並從singletonFactories中刪除B的對象工廠->將B填充到A的屬性中->將成品A放入singletonObjects並刪除A的對象工廠。
同樣,這樣的流程也適用於普通的IOC已經有並發的場景,但如果A上加個切面(AOP)的話,這種情況也無法滿足需求。
因為拿到ObjectFactory對象後,調用ObjectFactory.getObject()方法最終會調用getEarlyBeanReference()方法,getEarlyBeanReference這個方法每次從三級緩存中拿到singleFactory對象,執行getObject()方法又會產生新的代理對象
所有這裡我們要借助二級緩存來解決這個問題,將執行瞭singleFactory.getObject()產生的對象放到二級緩存中去,後面去二級緩存中拿,沒必要再執行一遍singletonFactory.getObject()方法再產生一個新的代理對象,保證始終隻有一個代理對象。
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) { // 最終會調用傳入的匿名內部類getEarlyBeanReference()方法,這裡面沒調用一次會生成一個新的代理對象 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; }
到此這篇關於關於Java Spring三級緩存和循環依賴的深入理解的文章就介紹到這瞭,更多相關Java Spring 三級緩存 循環依賴內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Spring解決循環依賴的方法(三級緩存)
- 聊聊Spring循環依賴三級緩存是否可以減少為二級緩存的情況
- 關於spring循環依賴問題及解決方案
- SpringBean依賴和三級緩存的案例講解
- Java之SSM中bean相關知識匯總案例講解