欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring源碼之循環(huán)依賴之三級緩存詳解

 更新時間:2022年02月09日 15:29:44   作者:一定會去到彩虹海的麥當(dāng)  
這篇文章主要為大家詳細(xì)介紹了Spring源碼之循環(huán)依賴之三級緩存,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

循環(huán)依賴

定義

循環(huán)依賴就 循環(huán)引用,就是兩個或多個 bean 相互之間的持有對方,比如 CircleA 引用 CircleB , CircleB 引用 CircleC, CircleC 引用 CircleA ,則它們最終反映為 個環(huán)。此處不是循環(huán)調(diào)用,循環(huán)調(diào)用是方法之間的環(huán)調(diào)用。

循環(huán)調(diào)用是無法解決的,除非有終結(jié)條件,否則就是死循環(huán),最終導(dǎo)致內(nèi)存溢出錯誤

三種循環(huán)依賴的情況

Spring容器將每一個正在創(chuàng)建的bean標(biāo)識符放在一個“當(dāng)前創(chuàng)建bean池”中,bean標(biāo)識符在創(chuàng)建過程中將一直保持在這個池中,因此如果在創(chuàng)建bean過程中發(fā)現(xiàn)自己已經(jīng)在“當(dāng)前創(chuàng)建bean池”里時,將拋出BeanCurrentlylnCreationException異常表示循環(huán)依賴;而對于創(chuàng)建完畢的bean將從“當(dāng)前創(chuàng)建bean池"中清除掉。

1.構(gòu)造器循環(huán)依賴

表示通過構(gòu)造器注入構(gòu)成的循環(huán)依賴,此依賴是無法解決的,只能拋出異常

2.settler循環(huán)依賴

表示通過setter注入方式構(gòu)成的循環(huán)依賴。對于setter注入造成的依賴是通過Spring容器提前暴露剛完成構(gòu)造器注入但未完成其他步驟(如setter注人)的bean來完成的,而且只能解決單例作用域的bean循環(huán)依賴。通過提前暴露一個單例工廠方法,從而使其他bean能引用到該bean。

3.prototype范圍的依賴處理

對于"prototype"作用域bean,Spring容器無法完成依賴注人,因為Spring容器不進(jìn)行緩存"prototype"作用域的bean,因此無法提前暴露一個創(chuàng)建中的bean。

三級緩存機(jī)制

  • 定義 一級緩存用于存放已經(jīng)實例化、初始化完成的Bean,單例池-singletonObjects
  • 二級緩存用于存放已經(jīng)實例化,但未初始化的Bean.保證一個類多次循環(huán)依賴時僅構(gòu)建一次保證單例提前曝光早產(chǎn)bean池-earlySingletonObjects
  • 三級緩存用于存放該Bean的BeanFactory,當(dāng)加載一個Bean會先將該Bean包裝為BeanFactory放入三級緩存早期單例bean工廠池-singletonFactories
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	/** Cache of singleton objects: bean name --> bean instance */
	//用于存放完全初始化好的 bean從該緩存中取出的 bean可以直接使用
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
	/** Cache of singleton factories: bean name --> ObjectFactory */
	//存放 bean工廠對象解決循環(huán)依賴
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
	//存放原始的bean對象用于解決循環(huán)依賴,注意:存到里面的對象還沒有被填充屬性
	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
}

三級緩存

整體分析

創(chuàng)建Bean會先將該Bean的BeanFactory放到三級緩存中,以用來防止循環(huán)依賴問題.當(dāng)存在有A,B兩個Bean循環(huán)依賴時,創(chuàng)建流程如下

1.先創(chuàng)建BeanA,先實例化BeanA并包裝為BeanFactory并放入三級緩存中.

2.給BeanA進(jìn)行屬性填充時檢查依賴,發(fā)現(xiàn)BeanB未加載過,則先去加載BeanB

3.BeanB創(chuàng)建過程首先也要包裝成BeanFactory放到三級緩存,填充屬性時則是從三級緩存獲取Bean將BeanA填充進(jìn)去

4.。BeanB填充BeanA從三級緩存中的BeanAFacotry獲取BeanA

5.獲取主要通過ObjectFactory.getObject方法,該方法調(diào)用getEarlyBeanReference方法,他會創(chuàng)建Bean/Bean的代理并刪除BeanA的三級緩存,加入二級緩存

6.BeanB初始化完畢加入一級緩存,BeanA繼續(xù)執(zhí)行初始化,初始化完畢比較BeanA二級緩存和一級緩存是否一致,一致則加入一級緩存刪除二級緩存

img

源碼分析

此處以A、B類的互相依賴注入為例,在這里表達(dá)出關(guān)鍵代碼的走勢:

1.入口處即是實例化、初始化A這個單例BeanAbstractBeanFactory.doGetBean("a")

protected <T> T doGetBean(...){
	... 
	// 標(biāo)記beanName a是已經(jīng)創(chuàng)建過至少一次的~~~ 它會一直存留在緩存里不會被移除(除非拋出了異常)
	// 參見緩存Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256))
	if (!typeCheckOnly) {
		markBeanAsCreated(beanName);
	}
	// 此時a不存在任何一級緩存中,且不是在創(chuàng)建中  所以此處返回null
	// 此處若不為null,然后從緩存里拿就可以了(主要處理FactoryBean和BeanFactory情況吧)
	Object beanInstance = getSingleton(beanName, false);
	...
	// 這個getSingleton方法非常關(guān)鍵。
	//1、標(biāo)注a正在創(chuàng)建中~
	//2、調(diào)用singletonObject = singletonFactory.getObject();(實際上調(diào)用的是createBean()方法)  因此這一步最為關(guān)鍵
	//3、此時實例已經(jīng)創(chuàng)建完成  會把a(bǔ)移除整整創(chuàng)建的緩存中
	//4、執(zhí)行addSingleton()添加進(jìn)去。(備注:注冊bean的接口方法為registerSingleton,它依賴于addSingleton方法)
	sharedInstance = getSingleton(beanName, () -> { ... return createBean(beanName, mbd, args); });
}

下面進(jìn)入到最為復(fù)雜的AbstractAutowireCapableBeanFactory.createBean/doCreateBean()環(huán)節(jié),創(chuàng)建A的實例

protected Object doCreateBean(){
	...
	// 使用構(gòu)造器/工廠方法   instanceWrapper是一個BeanWrapper
	instanceWrapper = createBeanInstance(beanName, mbd, args);
	// 此處bean為"原始Bean"   也就是這里的A實例對象:A@1234
	final Object bean = instanceWrapper.getWrappedInstance();
	...
	// 是否要提前暴露(允許循環(huán)依賴)  現(xiàn)在此處A是被允許的
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
	// 允許暴露,就把A綁定在ObjectFactory上,注冊到三級緩存`singletonFactories`里面去保存著
	// Tips:這里后置處理器的getEarlyBeanReference方法會被促發(fā),自動代理創(chuàng)建器在此處創(chuàng)建代理對象(注意執(zhí)行時機(jī) 為執(zhí)行三級緩存的時候)
	if (earlySingletonExposure) {
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	...
	// exposedObject 為最終返回的對象,此處為原始對象bean也就是A@1234,下面會有用處
	Object exposedObject = bean; 
	// 給A@1234屬性完成賦值,@Autowired在此處起作用~
	// 因此此處會調(diào)用getBean("b"),so 會重復(fù)上面步驟創(chuàng)建B類的實例
	// 此處我們假設(shè)B已經(jīng)創(chuàng)建好了 為B@5678
	// 需要注意的是在populateBean("b")的時候依賴有beanA,所以此時候調(diào)用getBean("a")最終會調(diào)用getSingleton("a"),
	//此時候上面說到的getEarlyBeanReference方法就會被執(zhí)行。這也解釋為何我們@Autowired是個代理對象,而不是普通對象的根本原因
	populateBean(beanName, mbd, instanceWrapper);
	// 實例化。這里會執(zhí)行后置處理器BeanPostProcessor的兩個方法
	// 此處注意:postProcessAfterInitialization()是有可能返回一個代理對象的,這樣exposedObject 就不再是原始對象了
	exposedObject = initializeBean(beanName, exposedObject, mbd);
	... // 至此,相當(dāng)于A@1234已經(jīng)實例化完成、初始化完成(屬性也全部賦值了~)
	// 這一步我把它理解為校驗:校驗:校驗是否有循環(huán)引用問題~~~~~
	if (earlySingletonExposure) {
		// 注意此處第二個參數(shù)傳的false,表示不去三級緩存里singletonFactories再去調(diào)用一次getObject()方法了~~~
		// 上面建講到了由于B在初始化的時候,會觸發(fā)A的ObjectFactory.getObject()  所以a此處已經(jīng)在二級緩存earlySingletonObjects里了
		// 因此此處返回A的實例:A@1234
		Object earlySingletonReference = getSingleton(beanName, false);
		if (earlySingletonReference != null) {
			// 這個等式表示,exposedObject若沒有再被代理過,這里就是相等的
			// 顯然此處我們的a對象的exposedObject它是沒有被代理過的  所以if會進(jìn)去~
			// 這種情況至此,就全部結(jié)束了~~~
			if (exposedObject == bean) {
				exposedObject = earlySingletonReference;
			}
			// 繼續(xù)以A為例,比如方法標(biāo)注了@Aysnc注解,exposedObject此時候就是一個代理對象,因此就會進(jìn)到這里來
			//hasDependentBean(beanName)是肯定為true,因為getDependentBeans(beanName)得到的是["b"]這個依賴
			else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
				String[] dependentBeans = getDependentBeans(beanName);
				Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
				// A@1234依賴的是["b"],所以此處去檢查b
				// 如果最終存在實際依賴的bean:actualDependentBeans不為空 那就拋出異常  證明循環(huán)引用了~
				for (String dependentBean : dependentBeans) {
					// 這個判斷原則是:如果此時候b并還沒有創(chuàng)建好,this.alreadyCreated.contains(beanName)=true表示此bean已經(jīng)被創(chuàng)建過,就返回false
					// 若該bean沒有在alreadyCreated緩存里,就是說沒被創(chuàng)建過(其實只有CreatedForTypeCheckOnly才會是此倉庫)
					if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
						actualDependentBeans.add(dependentBean);
					}
				}
				if (!actualDependentBeans.isEmpty()) {
					throw new BeanCurrentlyInCreationException(beanName,
							"Bean with name '" + beanName + "' has been injected into other beans [" +
							StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
							"] in its raw version as part of a circular reference, but has eventually been " +
							"wrapped. This means that said other beans do not use the final version of the " +
							"bean. This is often the result of over-eager type matching - consider using " +
							"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
				}
			}
		}
	}
}

三級緩存加載bean流程圖

依舊以上面A、B類使用屬性field注入循環(huán)依賴的例子為例,對整個流程做文字步驟總結(jié)如下:

1.使用context.getBean(A.class),旨在獲取容器內(nèi)的單例A(若A不存在,就會走A這個Bean的創(chuàng)建流程),顯然初次獲取A是不存在的,因此走A的創(chuàng)建之路~

2.實例化A(注意此處僅僅是實例化),并將它放進(jìn)緩存(此時A已經(jīng)實例化完成,已經(jīng)可以被引用了)

3.初始化A:@Autowired依賴注入B(此時需要去容器內(nèi)獲取B)

4.為了完成依賴注入B,會通過getBean(B)去容器內(nèi)找B。但此時B在容器內(nèi)不存在,就走向B的創(chuàng)建之路~

5.實例化B,并將其放入緩存。(此時B也能夠被引用了)

6.初始化B,@Autowired依賴注入A(此時需要去容器內(nèi)獲取A)

7.此處重要:初始化B時會調(diào)用getBean(A)去容器內(nèi)找到A,上面我們已經(jīng)說過了此時候因為A已經(jīng)實例化完成了并且放進(jìn)了緩存里,所以這個時候去看緩存里是已經(jīng)存在A的引用了的,所以getBean(A)能夠正常返回

8.B初始化成功(此時已經(jīng)注入A成功了,已成功持有A的引用了),return(注意此處return相當(dāng)于是返回最上面的getBean(B)這句代碼,回到了初始化A的流程中~)。

9.因為B實例已經(jīng)成功返回了,因此最終A也初始化成功

10.到此,B持有的已經(jīng)是初始化完成的A,A持有的也是初始化完成的B

面試題

Spring為什么定三級緩存(二級緩存也可以解決循環(huán)依賴的問題)

首先當(dāng)Bean未有循環(huán)依賴三級緩存是沒有什么意義的,當(dāng)有循環(huán)依賴但Bean并沒有AOP代理,則會直接返回原對象,也沒有什么意義。主要在當(dāng)Bean存在循環(huán)依賴并且還有AOP代理時,三級緩存才有效果,三級緩存主要預(yù)防Bean有依賴時還可以完成代理增強(qiáng)

而本身Spring設(shè)計Bean的代理增強(qiáng)是在Bean初始化完成后的AnnotationAwareAspectJ***Creator后置處理器中完成的。提前執(zhí)行則和設(shè)計思路相違背。所以三級緩存主要起預(yù)防循環(huán)依賴作用,可能是一個補(bǔ)丁機(jī)制

參考鏈接:

你知道怎么用Spring的三級緩存解決循環(huán)依賴嗎

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!   

相關(guān)文章

最新評論