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

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

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

概述

我們?cè)谌粘5募夹g(shù)交流中經(jīng)常會(huì)提到Spring循環(huán)依賴,聽起來挺高大尚的,那Spring到底是如何實(shí)現(xiàn)的呢?下面我們就來一一揭秘。

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

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

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

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

Spring三級(jí)緩存介紹

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

Spring中的如何解決的呢?

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

這就是常說的提前暴露對(duì)象。

spring中的三級(jí)緩存分別是什么

在spring源碼的DefaultSingletonBeanRegistry.java類中

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

三級(jí)緩存解決循環(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();
    }
}

關(guān)鍵代碼

1、添加A對(duì)象工廠到三級(jí)緩存

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

2、填充屬性b,從beanFactory中獲取b對(duì)象

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

3、添加B對(duì)象工廠到三級(jí)緩存

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

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

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

5、添加B對(duì)象到一級(jí)緩存,同時(shí)刪除二級(jí)、三級(jí)緩存

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

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

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

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

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

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

2、填充A對(duì)象的屬性b時(shí),先實(shí)例化B對(duì)象b,b對(duì)象的ObjectFactory放入三級(jí)緩存。

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

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

5、a對(duì)象從一級(jí)緩存獲取b1,并填充b屬性。

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

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

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

相關(guān)文章

  • 淺談SSH框架中spring的原理

    淺談SSH框架中spring的原理

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

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

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

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

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

    深入了解Java排序算法

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

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

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

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

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

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

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

    Java實(shí)現(xiàn)復(fù)雜的進(jìn)制轉(zhuǎn)換器功能示例

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

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

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

    SpringBoot?項(xiàng)目瘦身maven/gradle詳解

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

最新評(píng)論