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

Spring循環(huán)依賴實現(xiàn)過程揭秘

 更新時間:2023年01月13日 10:56:08   作者:融極  
這篇文章主要介紹了Spring循環(huán)依賴實現(xiàn)過程,Spring的解決循環(huán)依賴是有前置條件的,要解決循環(huán)依賴我們首先要了解Spring Bean對象的創(chuàng)建過程和依賴注入的方式

概述

我們在日常的技術交流中經(jīng)常會提到Spring循環(huán)依賴,聽起來挺高大尚的,那Spring到底是如何實現(xiàn)的呢?下面我們就來一一揭秘。

什么是循環(huán)依賴

如上圖所示,A對象中包含B對象的引用,同時B對象中包含A對象的引用;也就是在創(chuàng)建A的過程中需要同時創(chuàng)建B對象,這就是所謂的循環(huán)依賴。

下面是普通對象創(chuàng)建的流程圖,也正是循環(huán)依賴產(chǎn)生的根因。

可以看到A對象創(chuàng)建依賴B對象創(chuàng)建,B對象創(chuàng)建依賴A對象創(chuàng)建,形成了閉環(huán),造成死循環(huán)了。

Spring三級緩存介紹

上圖形成了一個閉環(huán),如果想解決這個問題,那么就必須保證不會出現(xiàn)第二次創(chuàng)建A對象這個步驟,也就是說從容器中獲取A的時候必須要能夠獲取到。

Spring中的如何解決的呢?

在Spring中,對象的創(chuàng)建可以分為實例化和初始化,實例化好但未完成初始化的對象是可以直接給其他對象引用的,所以此時可以做一件事,把完成實例化但未初始化的對象提前暴露出去,讓其他對象能夠進行引用,就完成了這個閉環(huán)的解環(huán)操作。

這就是常說的提前暴露對象。

spring中的三級緩存分別是什么

在spring源碼的DefaultSingletonBeanRegistry.java類中

/**
* 一級緩存
* 用于保存beanName和創(chuàng)建bean實例之間的關系,是已經(jīng)實例化和初始化完成的bean
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 二級緩存
* 用于保存beanName和創(chuàng)建bean實例之間的關系,與singletonObject的區(qū)別是這里面的bean是半成品,只完成實例化沒有完成初始化;
* 與singletonFactories的區(qū)別是,當一個單例bean被放到這里之后,那么當bean還在創(chuàng)建過程中就可以通過getBean方法獲取到,可以方便進行循環(huán)依賴的檢測。
*/
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/**
* 三級緩存
* 用于保存beanName和和創(chuàng)建bean的工廠之間的關系,ObjectFactory就是一個Lambda表達式。
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);

三級緩存解決循環(huán)依賴流程

示例

@Component
public class A {
    @Autowired
    private B b;
    public B getB() {
        return b;
    }
    public void setB(B b) {
        this.b = b;
    }
}
@Component
public class B {
    @Autowired
    private A a;
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
}
@Configuration
@ComponentScan(basePackages = "com.mashibing.selfcycle")
public class CycleConfig {
}
public class TestCycle {
    public static void main(String[] args) {
        final AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(CycleConfig.class);
        ac.close();
    }
}

關鍵代碼

1、添加A對象工廠到三級緩存

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// 為避免后期循環(huán)依賴,可以在bean初始化完成前將創(chuàng)建實例的ObjectFactory加入工廠
// bean是實例化后的bean對象
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	// 默認最終公開的對象是bean,通過createBeanInstance創(chuàng)建出來的普通對象
	Object exposedObject = bean;
	// mbd的systhetic屬性:設置此bean定義是否是"synthetic",一般是指只有AOP相關的pointCut配置或者Advice配置才會將 synthetic設置為true
	// 如果mdb不是synthetic且此工廠擁有InstantiationAwareBeanPostProcessor
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		// 遍歷工廠內(nèi)的所有后處理器
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			// 如果bp是SmartInstantiationAwareBeanPostProcessor實例
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				// 讓exposedObject經(jīng)過每個SmartInstantiationAwareBeanPostProcessor的包裝
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
}

2、填充屬性b,從beanFactory中獲取b對象

	public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
			throws BeansException {
		return beanFactory.getBean(beanName);
	}

3、添加B對象工廠到三級緩存

源代碼流程與《1、添加A對象工廠到三級緩存,A的實例化對象到二級緩存》 一致。

4、添加B對象的屬性a,從beanFactory中獲取a對象

DefaultSingleBeanRegistry.java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// Quick check for existing instance without full singleton lock
	// 從單例對象緩存中獲取beanName對應的單例對象
	Object singletonObject = this.singletonObjects.get(beanName);
	// 如果單例對象緩存中沒有,并且該beanName對應的單例bean正在創(chuàng)建中
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		//從早期單例對象緩存中獲取單例對象(之所稱成為早期單例對象,是因為earlySingletonObjects里
		// 的對象的都是通過提前曝光的ObjectFactory創(chuàng)建出來的,還未進行屬性填充等操作)
		singletonObject = this.earlySingletonObjects.get(beanName);
		// 如果在早期單例對象緩存中也沒有,并且允許創(chuàng)建早期單例對象引用
		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) {
						// 當某些方法需要提前初始化的時候則會調(diào)用addSingletonFactory方法將對應的ObjectFactory初始化策略存儲在singletonFactories
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
							// 如果存在單例對象工廠,則通過工廠創(chuàng)建一個單例對象
							singletonObject = singletonFactory.getObject();
							// 記錄在緩存中,二級緩存和三級緩存的對象不能同時存在
							this.earlySingletonObjects.put(beanName, singletonObject);
							// 從三級緩存中移除
							this.singletonFactories.remove(beanName);
						}
					}
				}
			}
		}
	}
	return singletonObject;
}

5、添加B對象到一級緩存,同時刪除二級、三級緩存

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton

	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			// 將映射關系添加到單例對象的高速緩存中
			this.singletonObjects.put(beanName, singletonObject);
			// 移除beanName在單例工廠緩存中的數(shù)據(jù)
			this.singletonFactories.remove(beanName);
			// 移除beanName在早期單例對象的高速緩存的數(shù)據(jù)
			this.earlySingletonObjects.remove(beanName);
			// 將beanName添加到已注冊的單例集中
			this.registeredSingletons.add(beanName);
		}
	}

6、A對象賦值b屬性,添加到一級緩存,完成A對象的創(chuàng)建 流程圖

理論上使用二級緩存就能解決上面的循環(huán)依賴問題,為什么要三級緩存呢?

三級緩存存放的是objectFactory,并不是bean對象,也就是說是用來創(chuàng)建bean對象的。 對于動態(tài)代理對象(比如切面切到的類),需要使用三級緩存來生成代理對象。 比如,如果A,B都被切點切中了:

1、A創(chuàng)建時首先生成A的實例化對象a,a對象的ObjectFactory放入三級緩存。

2、填充A對象的屬性b時,先實例化B對象b,b對象的ObjectFactory放入三級緩存。

3、b對象填充a屬性時,如果發(fā)現(xiàn)a需要代理,根據(jù)三級緩存生成a的代理對象a1。

4、b對象通過a1填充a屬性,在initialBean的時候走的beanPostProcessor方法是,生成b的代理對象b1,同時b1的a屬性是a1。

5、a對象從一級緩存獲取b1,并填充b屬性。

6、a對象在initialBean的時候走的beanPostProcessor方法是,從二級緩存獲取a1對象,同時a的b屬性是a1。

7、最后一級緩存中存放的就是a1,b1;a1的b屬性是b1,b1的a屬性是a1。

到此這篇關于Spring循環(huán)依賴實現(xiàn)過程揭秘的文章就介紹到這了,更多相關Spring循環(huán)依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 淺談SSH框架中spring的原理

    淺談SSH框架中spring的原理

    下面小編就為大家?guī)硪黄獪\談SSH框架中spring的原理。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01
  • java poi sax方式處理大數(shù)據(jù)量excel文件

    java poi sax方式處理大數(shù)據(jù)量excel文件

    這篇文章主要介紹了java poi sax方式處理大數(shù)據(jù)量excel文件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-01-01
  • Java實戰(zhàn)之城市多音字處理

    Java實戰(zhàn)之城市多音字處理

    這篇文章主要介紹了Java實戰(zhàn)之城市多音字處理,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • 深入了解Java排序算法

    深入了解Java排序算法

    本文主要介紹了深入了解Java排序算法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2007-03-03
  • springboot controller 增加指定前綴的兩種實現(xiàn)方法

    springboot controller 增加指定前綴的兩種實現(xiàn)方法

    這篇文章主要介紹了springboot controller 增加指定前綴的兩種實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 2021最新IDEA的各種快捷鍵匯總

    2021最新IDEA的各種快捷鍵匯總

    掌握idea的各種快捷鍵,可以幫助我們開發(fā)程序,今天小編給大家?guī)韼追N比較常用的idea快捷鍵及一些快捷鍵介紹,對idea快捷鍵相關知識,感興趣的朋友一起看看吧
    2021-05-05
  • springboot項目打包鏡像方式以及區(qū)分環(huán)境打包的方法

    springboot項目打包鏡像方式以及區(qū)分環(huán)境打包的方法

    本文主要介紹了springboot項目打包鏡像方式以及區(qū)分環(huán)境打包的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-03-03
  • Java實現(xiàn)復雜的進制轉換器功能示例

    Java實現(xiàn)復雜的進制轉換器功能示例

    這篇文章主要介紹了Java實現(xiàn)復雜的進制轉換器功能,結合實例形式分析了java數(shù)學運算的相關實現(xiàn)技巧,需要的朋友可以參考下
    2017-01-01
  • 解決IntelliJ?IDEA輸出中文顯示為問號問題的有效方法

    解決IntelliJ?IDEA輸出中文顯示為問號問題的有效方法

    最近剛學到文件字節(jié)流這里,但輸出中文時,出現(xiàn)了控制臺輸出問號的情況,所以下面這篇文章主要給大家介紹了關于如何解決IntelliJ?IDEA輸出中文顯示為問號問題的有效方法,需要的朋友可以參考下
    2022-07-07
  • SpringBoot?項目瘦身maven/gradle詳解

    SpringBoot?項目瘦身maven/gradle詳解

    這篇文章主要介紹了SpringBoot項目瘦身(maven/gradle),本文結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-01-01

最新評論