最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring boot啟動(dòng)流程之解決循環(huán)依賴的方法

 更新時(shí)間:2024年02月06日 12:05:47   作者:后仰大風(fēng)車  
循環(huán)依賴,指的是兩個(gè)bean之間相互依賴,形成了一個(gè)循環(huán),spring解決循環(huán)依賴的方式是在bean的實(shí)例化完成之后,所以不要在構(gòu)造方法中引入循環(huán)依賴,因?yàn)檫@時(shí)對(duì)象還沒有實(shí)例化,spring也無法解決,本文給大家介紹Spring boot循環(huán)依賴的解決方法,一起看看吧

循環(huán)依賴,指的是兩個(gè)bean之間相互依賴,形成了一個(gè)循環(huán)。
目前使用的spring版本中,在啟動(dòng)時(shí)默認(rèn)關(guān)閉了循環(huán)依賴。假設(shè)代碼中兩個(gè)bean相互使用@Autowired注解進(jìn)行自動(dòng)裝配,啟動(dòng)時(shí)會(huì)報(bào)錯(cuò)如下:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
翻譯過來就是:
不鼓勵(lì)使用循環(huán)引用,默認(rèn)情況下禁止使用循環(huán)引用。更新應(yīng)用程序以刪除bean之間的依賴循環(huán)。作為最后的手段,可以通過設(shè)置spring.main.allow-circular-references為true自動(dòng)打破循環(huán)。

spring還是支持循環(huán)依賴的,但前提是我們要手動(dòng)將其打開。我們?cè)谂渲梦募性O(shè)置屬性 spring.main.allow-circular-references = true。

解決循環(huán)依賴方法:

1、在bean A實(shí)例化完成后,如果檢測到支持循環(huán)依賴,會(huì)先把A緩存起來

// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//判斷當(dāng)前bean為單例,且允許循環(huán)依賴,且該bean正在創(chuàng)建中
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

addSingletonFactory方法的第二個(gè)參數(shù),傳入的是一個(gè)實(shí)現(xiàn)了ObjectFactory<?>函數(shù)式接口的lambda表達(dá)式,表達(dá)式中調(diào)用的是getEarlyBeanReference方法,獲取bean的早期引用。

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			/*
			如果 singletonObjects 中不存在當(dāng)前bean(當(dāng)前bean還在創(chuàng)建,還沒生成最終的單例對(duì)象,顯然成立),則:
			1)把當(dāng)前的singletonFactory放入singletonFactories變量中(把獲取早期引用的lambda表達(dá)式放進(jìn)三級(jí)緩存);
			2)把當(dāng)前bean從earlySingletonObjects變量中移除(清理二級(jí)緩存,確保在循環(huán)引用觸發(fā)時(shí)才生成早期引用;實(shí)際這里本來也沒有);
			3)把當(dāng)前beanName放入registeredSingletons變量中。
			*/
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

這里可能還看不出來什么頭緒,繼續(xù)往下走,在bean A初始化的時(shí)候,會(huì)自動(dòng)裝配依賴的bean B、C等,而在B、C初始化時(shí)又會(huì)自動(dòng)裝配它們所依賴的A(當(dāng)然B、C在實(shí)例化之后也會(huì)和A一樣先緩存起來),但這個(gè)時(shí)候A也正在創(chuàng)建中,最終的對(duì)象肯定是拿不到的,這時(shí)候就考慮生成一個(gè)A的早期引用,先提供給B、C。

2、在doGetBean方法獲取bean A的時(shí)候,會(huì)先調(diào)用getSingleton方法查找緩存,部分源碼如下:

// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

接下來看getSingleton方法:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		//先從singletonObjects中獲取bean A,之前說過顯然獲取不到
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			//再從earlySingletonObjects中獲取bean,剛開始也是沒有的
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							//最后從singletonFactories里拿到singletonFactory
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								//調(diào)用getObject方法獲取早期引用,放進(jìn)earlySingletonObjects,清理singletonFactories
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

到這里三級(jí)緩存的結(jié)構(gòu)就比較清晰了:

一級(jí)緩存:singletonObject,這里存放的是最終初始化完成的bean對(duì)象。
也就是說bean初始化完成后 ,下次再拿的時(shí)候,可以直接從緩存里找,所以這是一個(gè)通用緩存,并非專門為循環(huán)依賴而設(shè)計(jì)。

二級(jí)緩存:earlySingletonObjects,存放完成了實(shí)例化,但還沒有完成初始化的半成品bean對(duì)象,其實(shí)就是相當(dāng)于把不完整的對(duì)象提前暴露了出來。
比如當(dāng)bean A在初始化注入bean B、C的時(shí)候,B、C的初始化又都依賴了A,這時(shí)候從一級(jí)緩存里找A肯定是找不到的,于是就會(huì)下探到二級(jí)緩存,如果拿到了A,doGetBean方法就會(huì)返回而不是又進(jìn)入A的初始化,相當(dāng)于這個(gè)循環(huán)被切斷了。然后B、C就能夠完成初始化,最終A也能完成初始化。

三級(jí)緩存:singletonFactories,對(duì)象工廠,存放的是獲取半成品bean對(duì)象的方法實(shí)現(xiàn)。
二級(jí)緩存的數(shù)據(jù)并不是憑空產(chǎn)生的,而是由于一開始訪問二級(jí)緩存找不到數(shù)據(jù),然后下探到三級(jí)緩存,調(diào)用了getObject方法,拿到半成品對(duì)象后才放進(jìn)去的,然后其他bean才可以直接從二級(jí)緩存里面取。
二級(jí)緩存放入數(shù)據(jù)后,三級(jí)緩存對(duì)應(yīng)的數(shù)據(jù)不再需要,立即被移除。

singletonFactory.getObject()方法其實(shí)就是lambda表達(dá)式的實(shí)現(xiàn),調(diào)用了getEarlyBeanReference方法:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
				exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
		return exposedObject;
	}

這里調(diào)用 SmartInstantiationAwareBeanPostProcessor 的 getEarlyBeanReference方法,獲取了bean實(shí)例的早期引用,其實(shí)就是還沒有完成初始化的半成品bean對(duì)象本身(也可能是創(chuàng)建了一個(gè)它的代理對(duì)象)。

能不能不要三級(jí)緩存,每次實(shí)例化bean之后,直接生成一個(gè)早期引用放到二級(jí)緩存中,方便循環(huán)依賴?
可以但不合理,早期引用是可以用來解決循環(huán)依賴問題,但實(shí)際上spring默認(rèn)是不推薦使用循環(huán)依賴的,如果不結(jié)合實(shí)際情況是否需要循環(huán)依賴,直接緩存一個(gè)對(duì)象,這種設(shè)計(jì)顯然有問題。

能不能不要二級(jí)緩存,每次出現(xiàn)循環(huán)依賴的時(shí)候,直接調(diào)用三級(jí)緩存的方法獲取早期引用?
不行,首先是重復(fù)調(diào)用,有可能每次獲取早期引用的工作都是完全重復(fù)的;其實(shí)如果是一個(gè)需要AOP動(dòng)態(tài)代理的bean對(duì)象,每次都會(huì)產(chǎn)生一個(gè)新的代理對(duì)象,不符合單例的設(shè)計(jì)原則。

還有個(gè)細(xì)節(jié),就是當(dāng)一個(gè)bean初始化完成,拿到單例對(duì)象之后,會(huì)調(diào)用addSingleton方法:

protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

這里面不僅將對(duì)象放進(jìn)了一級(jí)緩存singletonObjects,還同時(shí)清理了對(duì)應(yīng)的二、三級(jí)緩存。

最后要注意的是,spring解決循環(huán)依賴的方式是在bean的實(shí)例化完成之后,所以不要在構(gòu)造方法中引入循環(huán)依賴,因?yàn)檫@時(shí)對(duì)象還沒有實(shí)例化,spring也無法解決。

到此這篇關(guān)于Spring boot啟動(dòng)流程-解決循環(huán)依賴的文章就介紹到這了,更多相關(guān)Spring boot循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java基礎(chǔ)-封裝和繼承

    Java基礎(chǔ)-封裝和繼承

    這篇文章主要介紹了Java面向?qū)ο缶幊蹋ǚ庋b/繼承/多態(tài))實(shí)例解析的相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下希望可以幫助到你
    2021-07-07
  • CentOS 7系統(tǒng)下配置自定義JDK的教程

    CentOS 7系統(tǒng)下配置自定義JDK的教程

    這篇文章主要給大家介紹了在CentOS 7系統(tǒng)下配置自定義JDK的教程,文中將配置的方法教程介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-06-06
  • 對(duì)SpringMVC的@RequestParam的解釋

    對(duì)SpringMVC的@RequestParam的解釋

    下面小編就為大家?guī)硪黄獙?duì)SpringMVC的@RequestParam的解釋。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • java操作xml的方法匯總及解析

    java操作xml的方法匯總及解析

    這篇文章主要介紹了java操作xml的方法匯總及解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Spring Security認(rèn)證機(jī)制源碼層探究

    Spring Security認(rèn)證機(jī)制源碼層探究

    SpringSecurity是基于Filter實(shí)現(xiàn)認(rèn)證和授權(quán),底層通過FilterChainProxy代理去調(diào)用各種Filter(Filter鏈),F(xiàn)ilter通過調(diào)用AuthenticationManager完成認(rèn)證 ,通過調(diào)用AccessDecisionManager完成授權(quán)
    2023-03-03
  • Java8中的Stream?流實(shí)踐操作

    Java8中的Stream?流實(shí)踐操作

    這篇文章主要介紹了Java8中的Stream?流實(shí)踐操作,Stream?是?java8?中處理集合的抽象概念,可以執(zhí)行非常復(fù)雜的查詢、過濾和映射數(shù)據(jù)等操作,下文更多相關(guān)資料介紹,需要的朋友可以參考一下
    2022-05-05
  • Java Random.nextInt()方法原理解析

    Java Random.nextInt()方法原理解析

    這篇文章主要介紹了Java Random.nextInt()方法原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java?Stream流以及常用方法操作實(shí)例

    Java?Stream流以及常用方法操作實(shí)例

    Stream是對(duì)Java中集合的一種增強(qiáng)方式,使用它可以將集合的處理過程變得更加簡潔、高效和易讀,這篇文章主要介紹了Java?Stream流以及常用方法的相關(guān)資料,需要的朋友可以參考下
    2025-08-08
  • Centos7安裝JDK1.8詳細(xì)過程實(shí)戰(zhàn)記錄

    Centos7安裝JDK1.8詳細(xì)過程實(shí)戰(zhàn)記錄

    這篇文章主要給大家介紹了關(guān)于Centos7安裝JDK1.8的相關(guān)資料,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-09-09
  • Java中的CAS和ABA問題說明

    Java中的CAS和ABA問題說明

    這篇文章主要介紹了Java中的CAS和ABA問題說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05

最新評(píng)論

宁都县| 万宁市| 五华县| 西乌珠穆沁旗| 吴桥县| 夏邑县| 海城市| 思南县| 台东县| 银川市| 香格里拉县| 江都市| 韩城市| 浦北县| 潼南县| 富裕县| 百色市| 同德县| 通城县| 广饶县| 高州市| 洛隆县| 科尔| 九龙城区| 宣城市| 崇礼县| 乃东县| 桦南县| 延川县| 通渭县| 富民县| 林芝县| 兴业县| 武安市| 余庆县| 阳高县| 高要市| 出国| 宕昌县| 寿阳县| 登封市|