亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一篇文章帶你理解Java Spring三級(jí)緩存和循環(huán)依賴(lài)

 更新時(shí)間:2021年09月22日 10:26:13   作者:小明の學(xué)習(xí)心得  
這篇文章主要介紹了淺談Spring 解決循環(huán)依賴(lài)必須要三級(jí)緩存嗎,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、什么是循環(huán)依賴(lài)?什么是三級(jí)緩存

什么是循環(huán)依賴(lài)】什么是循環(huán)依賴(lài)很好理解,當(dāng)我們代碼中出現(xiàn),形如BeanA類(lèi)中依賴(lài)注入BeanB類(lèi),BeanB類(lèi)依賴(lài)注入A類(lèi)時(shí),在IOC過(guò)程中creaBean實(shí)例化A之后,發(fā)現(xiàn)并不能直接initbeanA對(duì)象,需要注入B對(duì)象,發(fā)現(xiàn)對(duì)象池里還沒(méi)有B對(duì)象。通過(guò)構(gòu)建函數(shù)創(chuàng)建B對(duì)象的實(shí)例化。又因B對(duì)象需要注入A對(duì)象,發(fā)現(xiàn)對(duì)象池里還沒(méi)有A對(duì)象,就會(huì)套娃。

【三級(jí)緩存】三級(jí)緩存實(shí)際上就是三個(gè)Map對(duì)象,從存放對(duì)象的順序開(kāi)始

三級(jí)緩存singletonFactories存放ObjectFactory,傳入的是匿名內(nèi)部類(lèi),ObjectFactory.getObject() 方法最終會(huì)調(diào)用getEarlyBeanReference()進(jìn)行處理,返回創(chuàng)建bean實(shí)例化的lambda表達(dá)式。

二級(jí)緩存earlySingletonObjects存放bean,保存半成品bean實(shí)例,當(dāng)對(duì)象需要被AOP切面代時(shí),保存代理bean的實(shí)例beanProxy

一級(jí)緩存(單例池)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);

二、三級(jí)緩存如何解決循環(huán)依賴(lài)?

如何解決循環(huán)依賴(lài)】Spring解決循環(huán)依賴(lài)的核心思想在于提前曝光,首先創(chuàng)建實(shí)例化A,并在三級(jí)緩存singletonFactories中保存實(shí)例化A的lambda表達(dá)式以便獲取A實(shí)例,當(dāng)我沒(méi)有循環(huán)依賴(lài)和AOP時(shí),這個(gè)三級(jí)緩存singletonFactories是沒(méi)用在后續(xù)用到的。

但是當(dāng)我A對(duì)象需要注入B對(duì)象,發(fā)現(xiàn)緩存里還沒(méi)有B對(duì)象,創(chuàng)建B對(duì)象并又上述所說(shuō)添加進(jìn)三級(jí)緩存singletonFactories,B對(duì)象需要注入A對(duì)象,這時(shí)從半成品緩存里取到半成品對(duì)象A,通過(guò)緩存的lambda表達(dá)式創(chuàng)建A實(shí)例對(duì)象,并放到二級(jí)緩存earlySingletonObjects中。

此時(shí)B對(duì)象可以注入A對(duì)象實(shí)例和初始化自己,之后將完成品B對(duì)象放入完成品緩存singletonObjects。但是當(dāng)有aop時(shí),B對(duì)象還沒(méi)有把完成品B對(duì)象放入完成品緩存singletonObjects中,B對(duì)象初始化后需要進(jìn)行代理對(duì)象的創(chuàng)建,此時(shí)需要從singletonFactories獲取bean實(shí)例對(duì)象,進(jìn)行createProxy創(chuàng)建代理類(lèi)操作,這是會(huì)把proxy&B放入二級(jí)緩存earlySingletonObjects中。這時(shí)候才會(huì)把完整的B對(duì)象放入完成品一級(jí)緩存也叫單例池singletonObjects中,返回給A對(duì)象。

A對(duì)象繼續(xù)注入其他屬性和初始化,之后將完成品A對(duì)象放入完成品緩存。

三、使用二級(jí)緩存能不能解決循環(huán)依賴(lài)?

一定是不行,我們只保留二級(jí)緩存有兩個(gè)可能性保留一二singletonObjectsearlySingletonObjects,或者一三singletonObjectssingletonFactories

只保留一二singletonObjects和earlySingletonObjects

流程可以這樣走:實(shí)例化A ->將半成品的A放入earlySingletonObjects中 ->填充A的屬性時(shí)發(fā)現(xiàn)取不到B->實(shí)例化B->將半成品的B放入earlySingletonObjects中->從earlySingletonObjects中取出A填充B的屬性->將成品B放入singletonObjects,并從earlySingletonObjects中刪除B->將B填充到A的屬性中->將成品A放入singletonObjects并刪除earlySingletonObjects。

這樣的流程是線(xiàn)程安全的,不過(guò)如果A上加個(gè)切面(AOP),這種做法就沒(méi)法滿(mǎn)足需求了,因?yàn)?code>earlySingletonObjects中存放的都是原始對(duì)象,而我們需要注入的其實(shí)是A的代理對(duì)象。

【只保留一三singletonObjects和singletonFactories】

流程是這樣的:實(shí)例化A ->創(chuàng)建A的對(duì)象工廠并放入singletonFactories中 ->填充A的屬性時(shí)發(fā)現(xiàn)取不到B->實(shí)例化B->創(chuàng)建B的對(duì)象工廠并放入singletonFactories中->從singletonFactories中獲取A的對(duì)象工廠并獲取A填充到B中->將成品B放入singletonObjects,并從singletonFactories中刪除B的對(duì)象工廠->將B填充到A的屬性中->將成品A放入singletonObjects并刪除A的對(duì)象工廠。

同樣,這樣的流程也適用于普通的IOC已經(jīng)有并發(fā)的場(chǎng)景,但如果A上加個(gè)切面(AOP)的話(huà),這種情況也無(wú)法滿(mǎn)足需求。

因?yàn)槟玫絆bjectFactory對(duì)象后,調(diào)用ObjectFactory.getObject()方法最終會(huì)調(diào)用getEarlyBeanReference()方法,getEarlyBeanReference這個(gè)方法每次從三級(jí)緩存中拿到singleFactory對(duì)象,執(zhí)行g(shù)etObject()方法又會(huì)產(chǎn)生新的代理對(duì)象

所有這里我們要借助二級(jí)緩存來(lái)解決這個(gè)問(wèn)題,將執(zhí)行了singleFactory.getObject()產(chǎn)生的對(duì)象放到二級(jí)緩存中去,后面去二級(jí)緩存中拿,沒(méi)必要再執(zhí)行一遍singletonFactory.getObject()方法再產(chǎn)生一個(gè)新的代理對(duì)象,保證始終只有一個(gè)代理對(duì)象。

getSingleton()、getEarlyBeanReference() 源碼如下

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName); // 先從一級(jí)緩存拿
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName); // 拿二級(jí)緩存
				if (singletonObject == null && allowEarlyReference) {
                    // 拿三級(jí)緩存
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 
					if (singletonFactory != null) {
                        // 最終會(huì)調(diào)用傳入的匿名內(nèi)部類(lèi)getEarlyBeanReference()方法,這里面沒(méi)調(diào)用一次會(huì)生成一個(gè)新的代理對(duì)象
						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é)

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

相關(guān)文章

  • Spring Data JDBC介紹及實(shí)現(xiàn)代碼

    Spring Data JDBC介紹及實(shí)現(xiàn)代碼

    這篇文章主要介紹了Spring Data JDBC介紹及實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • java捕獲AOP級(jí)別的異常并將其傳遞到Controller層

    java捕獲AOP級(jí)別的異常并將其傳遞到Controller層

    如何在一個(gè)現(xiàn)代的Java應(yīng)用中,捕獲AOP(面向切面編程)級(jí)別的異常,并將這些異常傳遞到Controller層進(jìn)行合適的處理,異常處理在構(gòu)建可靠的應(yīng)用程序中起著關(guān)鍵作用,而AOP則可以幫助我們更好地管理和組織代碼,我們將深入研究如何結(jié)合AOP和異常處理來(lái)構(gòu)建健壯的應(yīng)用
    2023-09-09
  • Java導(dǎo)出CSV文件的方法

    Java導(dǎo)出CSV文件的方法

    這篇文章主要為大家詳細(xì)介紹了Java導(dǎo)出CSV文件的方法,分頁(yè)查詢(xún)大數(shù)據(jù)量,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • Java策略模式實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車(chē)功能

    Java策略模式實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車(chē)功能

    這篇文章主要介紹了Java策略模式實(shí)現(xiàn)簡(jiǎn)單地購(gòu)物車(chē),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java?設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解單例模式

    Java?設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解單例模式

    單例模式(Singleton?Pattern)是?Java?中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類(lèi)型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式
    2022-03-03
  • Java中Object用法詳解

    Java中Object用法詳解

    Java是面向?qū)ο蟮木幊陶Z(yǔ)言,而在面向?qū)ο笾校械腏ava類(lèi)都有一個(gè)共同的祖先類(lèi),這就是Object,那么Object都有哪些特性呢?今天小編就簡(jiǎn)單跟大家分析一下,感興趣的同學(xué)可以跟著小編一起學(xué)習(xí)
    2023-05-05
  • Java創(chuàng)建線(xiàn)程的兩種方式

    Java創(chuàng)建線(xiàn)程的兩種方式

    這篇文章主要介紹了Java創(chuàng)建線(xiàn)程的兩種方式,針對(duì)Java創(chuàng)建線(xiàn)程的兩種方式進(jìn)行比較,感興趣的小伙伴們可以參考一下
    2016-10-10
  • springBoot接入阿里云oss的實(shí)現(xiàn)步驟

    springBoot接入阿里云oss的實(shí)現(xiàn)步驟

    這篇文章主要介紹了springBoot接入阿里云oss的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Java8特性之用Stream流代替For循環(huán)操作詳解

    Java8特性之用Stream流代替For循環(huán)操作詳解

    這篇文章主要介紹了Stream流代替For循環(huán)進(jìn)行輸出,這樣可以使代碼更簡(jiǎn)潔,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-09-09
  • MyBatisPlus中CRUD使用方法詳解

    MyBatisPlus中CRUD使用方法詳解

    通用CRUD封裝,BaseMapper(opens new window)接口,為Mybatis-Plus啟動(dòng)時(shí)自動(dòng)解析實(shí)體表關(guān)系映射轉(zhuǎn)換為Mybatis內(nèi)部對(duì)象注入容器
    2023-01-01

最新評(píng)論