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

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

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

循環(huán)依賴,指的是兩個bean之間相互依賴,形成了一個循環(huán)。
目前使用的spring版本中,在啟動時默認關閉了循環(huán)依賴。假設代碼中兩個bean相互使用@Autowired注解進行自動裝配,啟動時會報錯如下:

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.
翻譯過來就是:
不鼓勵使用循環(huán)引用,默認情況下禁止使用循環(huán)引用。更新應用程序以刪除bean之間的依賴循環(huán)。作為最后的手段,可以通過設置spring.main.allow-circular-references為true自動打破循環(huán)。

spring還是支持循環(huán)依賴的,但前提是我們要手動將其打開。我們在配置文件中設置屬性 spring.main.allow-circular-references = true。

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

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

// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//判斷當前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方法的第二個參數,傳入的是一個實現了ObjectFactory<?>函數式接口的lambda表達式,表達式中調用的是getEarlyBeanReference方法,獲取bean的早期引用。

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

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

2、在doGetBean方法獲取bean A的時候,會先調用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) {
								//調用getObject方法獲取早期引用,放進earlySingletonObjects,清理singletonFactories
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

到這里三級緩存的結構就比較清晰了:

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

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

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

singletonFactory.getObject()方法其實就是lambda表達式的實現,調用了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;
	}

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

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

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

還有個細節(jié),就是當一個bean初始化完成,拿到單例對象之后,會調用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);
		}
	}

這里面不僅將對象放進了一級緩存singletonObjects,還同時清理了對應的二、三級緩存。

最后要注意的是,spring解決循環(huán)依賴的方式是在bean的實例化完成之后,所以不要在構造方法中引入循環(huán)依賴,因為這時對象還沒有實例化,spring也無法解決。

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

相關文章

最新評論