【十七】Spring IOC 三級緩存解決循環依賴

一、spring ioc的三級緩存

1.哪三級

singletonObjects:第一級緩存,裏面放置的是已經實例化好的單例對象,是單例緩存池(singletonObjects)

earlySingletonObjects:第二級緩存,裏面存放的是提前曝光的單例對象,早期對象(earlySingletonObjects)。簡單粗暴的說就是new了對象了,但是這個對象還沒填充屬性;

singletonFactories:第三級緩存,裏面存放的是將要被實例化的對象的對象工廠(存放 bean 工廠對象),是一個包裹對象ObjectFactory(registeredSingletons),通過getObject獲取到早期對象。

2.作用:

spring ioc的三級緩存解決引用循環依賴,不解決構造方法注入的循環依賴,不解決非單例的循環引用

3.到底怎麼解決的循環依賴

最簡單的例子是,A引用了B,B引用了A。

直接上一個我覺得畫得很好的創建bean的圖,圖來自:https://www.jianshu.com/p/f0c005c7354b

圖片解釋:

先看創建beanA,

第四步getSingleton從緩存中獲取beanA,沒有嘛,這纔開始實例化嘞,緩存中沒有咱就創建。

第八步就是創建了一個早期得beanA的對象。

第九步就是把這個早期對象放入緩存中,這裏我理解的是放在二級緩存earlySingletonObjects中的。

第十步填充對象,這個時候就解析出來beanA依賴了beanB,那就要去創建beanB的對象才能給beanA填充上

再看創建beanB

第四步getSingleton從緩存中獲取beanB,沒有嘛,這纔開始實例化嘞,緩存中沒有咱就創建。

第八步就是創建了一個早期得beanB的對象。

第九步就是把這個早期對象放入緩存中,這裏我理解的是放在二級緩存earlySingletonObjects中的。

第十步填充對象,這個時候就解析出來beanB依賴了beanA,這個時候getSingleton下能拿到二級緩存earlySingletonObjects早期暴露的beanA。

衍生問題:

1.爲什麼不能解決決構造方法注入的循環依賴?

從上面的流程可以看出,調用構造器創建實例是在第八步createBeanInstance方法,而解決循環依賴是在第十步populateBean這個方法中,執行順序也決定了無法解決該種循環依賴

對於這種,如果喜歡使用lombok的@RequiredArgsConstructor註解的小夥伴就需要注意了,這個註解會生成一個帶所有屬性的構造方法。通過idea直接點開對應的class文件就可以看不看這個構造方法了。

2.爲什麼不能解決非單例對象的循環依賴?

非單例對象都沒有放到這三級緩存中.

3.爲什麼一定要三級緩存,二級行不行?
不行,二級緩存不能解決代理對象之間的循環依賴。

詳細解釋:

首先我最前面寫了:三級緩存是一個包裹對象ObjectFactory(registeredSingletons),通過調用它的getObject方法獲取到早期對象,也就是得到那個所謂的二級緩存,沒有填充的早期對象。

貼上圖中第四步的DefaultSingletonBeanRegistry#getSingleton方法源碼

	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) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}

獲取bean的時候,先從一級緩存(singletonObjects)中獲取,也就是先看它是否已經實例化完了已經被spring管理起了,如果有就直接返回。

如果一級緩存沒有,但是這個bean處於正在創建中,就從二級緩存(earlySingletonObjects)中拿,二級緩存有就返回二級緩存的。

如果二級緩存也沒有,但是這個bean是可以允許早期引用的,那就從三級緩存(singletonFactories)中拿,這裏三級緩存中拿出來的是個ObjectFactory,需要調用singletonFactory.getObject方法得到對象,這裏調用singletonFactory.getObject方法得到得就直接放入二級緩存中了。

那麼重點就是這個singletonFactory.getObject方法,我們可以看到它主要就是調用了

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference這個方法。

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
					if (exposedObject == null) {
						return null;
					}
				}
			}
		}
		return exposedObject;
	}

源碼中可以看到實際上是調用的SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference方法

好,重點來了。

SmartInstantiationAwareBeanPostProcessor接口有兩個主要的實現類,這次調用getEarlyBeanReference方法就根據不同的場景調用的這兩個不同的實現類

如果是調到了InstantiationAwareBeanPostProcessorAdapter這個實現類,那就直接返回bean,這裏看起來第三級緩存沒必要存在,然而並不是

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		return bean;
	}

如果調用到了AbstractAutoProxyCreator這個實現類,那就體現了第三級緩存存在的意義了,第三級緩存的存在就是解決代理對象之間的循環依賴

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.contains(cacheKey)) {
			this.earlyProxyReferences.add(cacheKey);
		}
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

這裏可以看到,一進來就先加入到了earlyProxyReferences緩存中,然後返回代理對象。

而在AbstractAutoProxyCreator類的postProcessAfterInitialization方法做代理時

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

而在AbstractAutoProxyCreator類的postProcessAfterInitialization方法做代理時,通過從earlyProxyReferences中看改對象是否早期就返回了代理,如果是那邊表示此時的bean就是代理了的,直接返回bean。如果不是,纔對這個對象做代理然後返回代理。

這裏又避免了代理對象循環引用的時候出現同一個對象被代理多次的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章