中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站建設(shè)全部流程包括備案免費(fèi)建站免費(fèi)網(wǎng)站

網(wǎng)站建設(shè)全部流程包括備案,免費(fèi)建站免費(fèi)網(wǎng)站,昆明網(wǎng)站開發(fā)多少錢,給企業(yè)做網(wǎng)站 工作前言 問:Spring 如何解決循環(huán)依賴? 答:Spring 通過提前曝光機(jī)制,利用三級(jí)緩存解決循環(huán)依賴(這原理還是挺簡單的,參考:三級(jí)緩存、圖解循環(huán)依賴原理) 再問:Spring 通過提前…

前言

問:Spring 如何解決循環(huán)依賴?
答:Spring 通過提前曝光機(jī)制,利用三級(jí)緩存解決循環(huán)依賴(這原理還是挺簡單的,參考:三級(jí)緩存、圖解循環(huán)依賴原理)
再問:Spring 通過提前曝光,直接曝光到二級(jí)緩存已經(jīng)可以解決循環(huán)依賴問題了,為什么一定要三級(jí)緩存?
再細(xì)問:如果循環(huán)依賴的時(shí)候,所有類又都需要 Spring AOP 自動(dòng)代理,那 Spring 如何提前曝光?曝光的是原始 bean 還是代理后的 bean?

這些問題算是 Spring 源碼的壓軸題了,如果這些問題都弄明白,恭喜你順利結(jié)業(yè) Spring 源碼了。先上圖,再分析源碼

源碼分析

進(jìn)入正題,在 Spring 創(chuàng)建 Bean 的核心代碼 doGetBean 中,在實(shí)例化 bean 之前,會(huì)先嘗試從三級(jí)緩存獲取 bean,這也是 Spring 解決循環(huán)依賴的開始

(一) 緩存中獲取 bean

// AbstractBeanFactory.java
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {final String beanName = transformedBeanName(name);Object bean;// 2. 嘗試從緩存中獲取beanObject sharedInstance = getSingleton(beanName);...
}

getSingleton:

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 從一級(jí)緩存獲取,key=beanName value=beanObject singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 從二級(jí)緩存獲取,key=beanName value=beansingletonObject = this.earlySingletonObjects.get(beanName);// 是否允許循環(huán)引用if (singletonObject == null && allowEarlyReference) {/*** 三級(jí)緩存獲取,key=beanName value=objectFactory,objectFactory中存儲(chǔ)getObject()方法用于獲取提前曝光的實(shí)例** 而為什么不直接將實(shí)例緩存到二級(jí)緩存,而要多此一舉將實(shí)例先封裝到objectFactory中?* 主要關(guān)鍵點(diǎn)在getObject()方法并非直接返回實(shí)例,而是對(duì)實(shí)例又使用* SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法對(duì)bean進(jìn)行處理** 也就是說,當(dāng)spring中存在該后置處理器,所有的單例bean在實(shí)例化后都會(huì)被進(jìn)行提前曝光到三級(jí)緩存中,* 但是并不是所有的bean都存在循環(huán)依賴,也就是三級(jí)緩存到二級(jí)緩存的步驟不一定都會(huì)被執(zhí)行,有可能曝光后直接創(chuàng)建完成,沒被提前引用過,* 就直接被加入到一級(jí)緩存中。因此可以確保只有提前曝光且被引用的bean才會(huì)進(jìn)行該后置處理*/ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {/*** 通過getObject()方法獲取bean,通過此方法獲取到的實(shí)例不單單是提前曝光出來的實(shí)例,* 它還經(jīng)過了SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法處理過。* 這也正是三級(jí)緩存存在的意義,可以通過重寫該后置處理器對(duì)提前曝光的實(shí)例,在被提前引用時(shí)進(jìn)行一些操作*/singletonObject = singletonFactory.getObject();// 將三級(jí)緩存生產(chǎn)的bean放入二級(jí)緩存中this.earlySingletonObjects.put(beanName, singletonObject);// 刪除三級(jí)緩存this.singletonFactories.remove(beanName);}}}}return singletonObject;}

三級(jí)緩存分別是:

singletonObject: 一級(jí)緩存,該緩存key = beanName, value = bean; 這里的 bean 是已經(jīng)創(chuàng)建完成的,該 bean 經(jīng)歷過實(shí)例化->屬性填充->初始化以及各類的后置處理。因此,一旦需要獲取 bean 時(shí),我們第一時(shí)間就會(huì)尋找一級(jí)緩存
earlySingletonObjects: 二級(jí)緩存,該緩存key = beanName, value = bean; 這里跟一級(jí)緩存的區(qū)別在于,該緩存所獲取到的 bean 是提前曝光出來的,是還沒創(chuàng)建完成的。也就是說獲取到的 bean 只能確保已經(jīng)進(jìn)行了實(shí)例化,但是屬性填充跟初始化肯定還沒有做完,因此該 bean 還沒創(chuàng)建完成,僅僅能作為指針提前曝光,被其他 bean 所引用
singletonFactories: 三級(jí)緩存,該緩存key = beanName, value = beanFactory; 在 bean 實(shí)例化完之后,屬性填充以及初始化之前,如果允許提前曝光,spring 會(huì)將實(shí)例化后的 bean 提前曝光,也就是把該 bean 轉(zhuǎn)換成 beanFactory 并加入到三級(jí)緩存。在需要引用提前曝光對(duì)象時(shí)再通過 singletonFactory.getObject()獲取。
這里拋出問題,如果我們直接將提前曝光的對(duì)象放到二級(jí)緩存 earlySingletonObjects,Spring 循環(huán)依賴時(shí)直接取就可以解決循環(huán)依賴了,為什么還要三級(jí)緩存 singletonFactory 然后再通過 getObject()來獲取呢?這不是多此一舉?

(二) 三級(jí)緩存的添加

我們回到添加三級(jí)緩存,添加 SingletonFactory 的地方,看看 getObject()到底做了什么操作

this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);
});

可以看到在返回 getObject()時(shí),多做了一步 getEarlyBeanReference 操作,這步操作是 BeanPostProcess 的一種,也就是給子類重寫的一個(gè)后處理器,目的是用于被提前引用時(shí)進(jìn)行拓展。即:曝光的時(shí)候并不調(diào)用該后置處理器,只有曝光,且被提前引用的時(shí)候才調(diào)用,確保了被提前引用這個(gè)時(shí)機(jī)觸發(fā)。

(三) 提前曝光代理 earlyProxyReferences

因此所有的重點(diǎn)都落到了 getEarlyBeanReference 上,getEarlyBeanReference 方法是 SmartInstantiationAwareBeanPostProcessor 所規(guī)定的接口。再通過 UML 的類圖查看實(shí)現(xiàn)類,僅有 AbstractAutoProxyCreator 進(jìn)行了實(shí)現(xiàn)。也就是說,除了用戶在子類重寫,否則僅有 AbstractAutoProxyCreator 一種情況

// AbstractAutoProxyCreator.java
public Object getEarlyBeanReference(Object bean, String beanName) {// 緩存當(dāng)前bean,表示該bean被提前代理了Object cacheKey = getCacheKey(bean.getClass(), beanName);this.earlyProxyReferences.put(cacheKey, bean);// 對(duì)bean進(jìn)行提前Spring AOP代理return wrapIfNecessary(bean, beanName, cacheKey);
}

wrapIfNecessary 是用于 Spring AOP 自動(dòng)代理的。Spring 將當(dāng)前 bean 緩存到 earlyProxyReferences 中標(biāo)識(shí)提前曝光的 bean 在被提前引用之前,然后進(jìn)行了 Spring AOP 代理。

但是經(jīng)過 Spring AOP 代理后的 bean 就已經(jīng)不再是原來的 bean 了,經(jīng)過代理后的 bean 是一個(gè)全新的 bean,也就是說代理前后的 2 個(gè) bean 連內(nèi)存地址都不一樣了。這時(shí)將再引出新的問題:B 提前引用 A 將引用到 A 的代理,這是符合常理的,但是最原始的 bean A 在 B 完成創(chuàng)建后將繼續(xù)創(chuàng)建,那么 Spring Ioc 最后返回的 Bean 是 Bean A 呢還是經(jīng)過代理后的 Bean 呢?

這個(gè)問題我們得回到 Spring AOP 代理,Spring AOP 代理時(shí)機(jī)有 2 個(gè):

當(dāng)自定義了 TargetSource,則在 bean 實(shí)例化前完成 Spring AOP 代理并且直接發(fā)生短路操作,返回 bean
正常情況下,都是在 bean 初始化后進(jìn)行 Spring AOP 代理
如果要加上今天說的提前曝光代理,getEarlyBeanReference 可以說 3 種
第一種情況就沒什么好探究的了,直接短路了,根本沒有后續(xù)操作。而我們關(guān)心的是第二種情況,在 Spring 初始化后置處理器中發(fā)生的 Spring AOP 代理

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {// 調(diào)用bean初始化后置處理器處理Object current = processor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;
}
// AbstractAutoProxyCreator.javapublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {// 獲取緩存keyObject cacheKey = getCacheKey(bean.getClass(), beanName);// 查看該bean是否被Spring AOP提前代理!而緩存的是原始的bean,因此如果bean被提前代理過,這此處會(huì)跳過// 如果bean沒有被提前代理過,則進(jìn)入AOP代理if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}

earlyProxyReferences 是不是有點(diǎn)熟悉,是的,這就是我們剛剛提前曝光并且進(jìn)行 Spring AOP 提前代理時(shí)緩存的原始 bean,如果緩存的原始 bean 跟當(dāng)前的 bean 是一至的,那么就不進(jìn)行 Spring AOP 代理了!返回原始的 bean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {try {///*** 4. 填充屬性* 如果@Autowired注解屬性,則在上方完成解析后,在這里完成注入** @Autowired* private Inner inner;*/populateBean(beanName, mbd, instanceWrapper);// 5. 初始化exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}// 6. 存在提前曝光情況下if (earlySingletonExposure) {// earlySingletonReference:二級(jí)緩存,緩存的是經(jīng)過提前曝光提前Spring AOP代理的beanObject earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {// exposedObject跟bean一樣,說明初始化操作沒用應(yīng)用Initialization后置處理器(指AOP操作)改變exposedObject// 主要是因?yàn)閑xposedObject如果提前代理過,就會(huì)跳過Spring AOP代理,所以exposedObject沒被改變,也就等于bean了if (exposedObject == bean) {// 將二級(jí)緩存中的提前AOP代理的bean賦值給exposedObject,并返回exposedObject = earlySingletonReference;}// 引用都不相等了,也就是現(xiàn)在的bean已經(jīng)不是當(dāng)時(shí)提前曝光的bean了else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {// dependentBeans也就是B, C, DString[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {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.");}}}}

這個(gè)時(shí)候我們需要理清一下 3 個(gè)變量

  1. **earlySingletonReference:**二級(jí)緩存,緩存的是經(jīng)過提前曝光提前 AOP 代理的 bean

  2. bean:這個(gè)就是經(jīng)過了實(shí)例化、填充、初始化的 bean

  3. exposedObject:這個(gè)是經(jīng)過了 AbstractAutoProxyCreator 的 postProcessAfterInitialization 處理過后的 bean,但是在其中因?yàn)榘l(fā)現(xiàn)當(dāng)前 bean 已經(jīng)被 earlyProxyReferences 緩存,所以并沒有進(jìn)行 AOP 處理,而是直接跳過,因此還是跟第 2 點(diǎn)一樣的 bean

理清這 3 個(gè)變量以后,就會(huì)發(fā)現(xiàn),exposedObject = earlySingletonReference;
AOP 代理過的 Bean 賦值給了 exposedObject 并返回,這時(shí)候用戶拿到的 bean 就是 AOP 代理過后的 bean 了,一切皆大歡喜了。

但是中間還有一個(gè)問題!提前曝光的 bean 在提前引用時(shí)被 Spring AOP 代理了,但是此時(shí)的 bean 只是經(jīng)過了實(shí)例化的 bean,還沒有進(jìn)行@Autowire 的注入啊!也就是說此時(shí)代理的 bean 里面自動(dòng)注入的屬性是空的!

(四) 提前 AOP 代理對(duì)象的 屬性填充、初始化
是的,確實(shí)在 Spring AOP 提前代理后沒有經(jīng)過屬性填充和初始化。那么這個(gè)代理又是如何保證依賴屬性的注入的呢?答案回到 Spring AOP 最早最早講的 JDK 動(dòng)態(tài)代理上找,JDK 動(dòng)態(tài)代理時(shí),會(huì)將目標(biāo)對(duì)象 target 保存在最后生成的代理proxy中,當(dāng)調(diào)用proxy中,當(dāng)調(diào)用proxy中,當(dāng)調(diào)用proxy 方法時(shí)會(huì)回調(diào) h.invoke,而 h.invoke 又會(huì)回調(diào)目標(biāo)對(duì)象 target 的原始方法。因此,其實(shí)在 Spring AOP 動(dòng)態(tài)代理時(shí),原始 bean 已經(jīng)被保存在提前曝光代理中了。而后原始 Bean 繼續(xù)完成屬性填充和初始化操作。因?yàn)?AOP 代理$proxy 中保存著 traget 也就是是原始 bean 的引用,因此后續(xù)原始 bean 的完善,也就相當(dāng)于 Spring AOP 中的 target 的完善,這樣就保證了 Spring AOP 的屬性填充與初始化了!

(五) 循環(huán)依賴遇上 Spring AOP 圖解

為了幫助大家理解,這里靈魂畫手畫張流程圖幫助大家理解
首先又 bean A,bean B,他們循環(huán)依賴注入,同時(shí) bean A 還需要被 Spring AOP 代理,例如事務(wù)管理或者日志之類的操作。
原始 bean A,bean B 圖中用 a,b 表示,而代理后的 bean A 我們用 aop.a 表示

http://www.risenshineclean.com/news/5573.html

相關(guān)文章:

  • 做網(wǎng)站優(yōu)化詞怎么選擇電商網(wǎng)站平臺(tái)有哪些
  • php網(wǎng)站維護(hù)刷關(guān)鍵詞排名
  • 建一個(gè)購物網(wǎng)站多少錢吳江seo網(wǎng)站優(yōu)化軟件
  • 百度做的網(wǎng)站能優(yōu)化嗎網(wǎng)站的seo 如何優(yōu)化
  • wordpress 做的網(wǎng)站全球中文網(wǎng)站排名
  • seo網(wǎng)站建設(shè)廈門2022千鋒教育培訓(xùn)收費(fèi)一覽表
  • 網(wǎng)站制作多少錢?個(gè)人網(wǎng)站制作教程
  • 類似情侶空間的網(wǎng)站開發(fā)制作網(wǎng)站平臺(tái)
  • 高端女裝有哪些品牌搜索引擎排名優(yōu)化seo
  • 銅川做網(wǎng)站電話顏色廣告
  • 燕郊網(wǎng)站建設(shè)公司企業(yè)網(wǎng)站推廣方案設(shè)計(jì)畢業(yè)設(shè)計(jì)
  • 南京 網(wǎng)站制作公司新網(wǎng)域名
  • 網(wǎng)站開發(fā)開票內(nèi)容寫什么產(chǎn)品關(guān)鍵詞大全
  • 林州網(wǎng)站建設(shè)拉新十大推廣app平臺(tái)
  • php網(wǎng)站優(yōu)點(diǎn)廈門seo培訓(xùn)
  • 做家教去哪個(gè)網(wǎng)站武漢seo價(jià)格
  • 企業(yè)網(wǎng)站建設(shè)與優(yōu)化深圳做推廣哪家比較好
  • 找大學(xué)生做家教去哪個(gè)網(wǎng)站找好關(guān)鍵詞seo深圳
  • 做網(wǎng)站搭建環(huán)境游戲推廣員一個(gè)月能賺多少
  • 網(wǎng)站登錄 效果代碼seo綜合查詢?cè)趺从玫?/a>
  • 濟(jì)南網(wǎng)站建設(shè)公網(wǎng)絡(luò)服務(wù)
  • 做網(wǎng)站推廣什么好友情鏈接網(wǎng)站源碼
  • 做公眾號(hào)的網(wǎng)站有哪些功能如何網(wǎng)站關(guān)鍵詞優(yōu)化
  • 做畢業(yè)論文的網(wǎng)站怎樣創(chuàng)建自己的網(wǎng)站
  • 網(wǎng)站建站網(wǎng)站的seo是什么意思?
  • 建網(wǎng)站盈利的幾種方式投放廣告
  • 哪些網(wǎng)站可以接設(shè)計(jì)的單子做培訓(xùn)管理平臺(tái)
  • 新的網(wǎng)站做淘寶客搜外網(wǎng)友情鏈接
  • 如何開始做b2b網(wǎng)站站長查詢工具
  • 江西省的建設(shè)廳官方網(wǎng)站社群營銷方案