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

spring如何快速穩(wěn)定解決循環(huán)依賴問題

 更新時(shí)間:2021年03月09日 14:56:24   作者:morris131  
這篇文章主要介紹了spring如何快速穩(wěn)定解決循環(huán)依賴問題,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

循環(huán)依賴其實(shí)就是循環(huán)引用,很多地方都說需要兩個(gè)或則兩個(gè)以上的bean互相持有對(duì)方最終形成閉環(huán)才是循環(huán)依賴,比如A依賴于B,B依賴于C,C又依賴于A。其實(shí)一個(gè)bean持有自己類型的屬性也會(huì)產(chǎn)生循環(huán)依賴。

setter singleton循環(huán)依賴

使用

SingleSetterBeanA依賴SingleSetterBeanB,SingleSetterBeanB依賴SingleSetterBeanA。

@Data
public class SingleSetterBeanA {
	@Autowired
	private SingleSetterBeanB singleSetterBeanB;
}
@Data
public class SingleSetterBeanB {
	@Autowired
	private SingleSetterBeanA singleSetterBeanA;
}

源碼分析

spring是通過三級(jí)緩存來解決循環(huán)依賴的,那么三級(jí)緩存是怎么工作的呢?

三級(jí)緩存對(duì)應(yīng)org.springframework.beans.factory.support.DefaultSingletonBeanRegistry類的三個(gè)屬性:

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 一級(jí)緩存

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 二級(jí)緩存

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); // 三級(jí)緩存

在這里插入圖片描述

對(duì)于setter注入造成的依賴是通過Spring容器提前暴露剛完成實(shí)例化但未完成初始化的bean來完成的,而且只能解決單例作用域的bean循環(huán)依賴。通過提前暴露一個(gè)單例工廠方法,從而使其他bean能引用到該bean,關(guān)鍵源碼如下所示:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

// 處理循環(huán)依賴,實(shí)例化后放入三級(jí)緩存
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));
}

bean實(shí)例化后放入三級(jí)緩存中:

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

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.singletonFactories.put(beanName, singletonFactory); // 三級(jí)緩存
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

放入三級(jí)緩存中的是ObjectFactory類型的lambda表達(dá)式:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference

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;
				/**
				 * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference(java.lang.Object, java.lang.String)
				 */
				// 使用AbstractAutoProxyCreator#getEarlyBeanReference創(chuàng)建代理對(duì)象
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
	return exposedObject;
}

構(gòu)造器參數(shù)循環(huán)依賴

通過構(gòu)造器注入構(gòu)成的循環(huán)依賴,此依賴是無法解決的,只能拋出BeanCurrentlyInCreationException異常表示循環(huán)依賴。

使用

@Data
public class SingleConstrutorBeanA {
	public SingleConstrutorBeanA(SingleConstrutorBeanB singleConstrutorBeanB) {
	}
}
@Data
public class SingleConstrutorBeanB {
	public SingleConstrutorBeanB(SingleConstrutorBeanA singleConstrutorBeanA) {
	}
}

上面的代碼運(yùn)行時(shí)會(huì)拋出如下異常:

... ...
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'singleConstrutorBeanB': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'singleConstrutorBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:805)
 at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1403)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1245)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:538)
 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:329)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
 at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:892)
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:796)
 ... 76 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'singleConstrutorBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
 at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:892)
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:796)
 ... 90 more

源碼分析

Spring容器會(huì)將每一個(gè)正在創(chuàng)建的Bean標(biāo)識(shí)符放在一個(gè)“當(dāng)前創(chuàng)建Bean池”中,Bean標(biāo)識(shí)符在創(chuàng)建過程中將一直保持在這個(gè)池中,因此如果在創(chuàng)建Bean過程中發(fā)現(xiàn)自己已經(jīng)在“當(dāng)前創(chuàng)建Bean池”里時(shí)將拋出BeanCurrentlyInCreationException異常表示循環(huán)依賴;而對(duì)于創(chuàng)建完畢的Bean將從“當(dāng)前創(chuàng)建Bean池”中清除掉。

protected void beforeSingletonCreation(String beanName) {
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
}

在這里插入圖片描述

@Lazy打破循環(huán)依賴

在上面的例子中只需要在SingleConstrutorBeanA或者SingleConstrutorBeanB的構(gòu)造方法上面加上@Lazy注解,就會(huì)發(fā)現(xiàn)不會(huì)拋出異常了,這又是為什么呢?

下面假設(shè)在SingleConstrutorBeanA的構(gòu)造方法上面加了@Lazy注解,在構(gòu)造B時(shí),發(fā)現(xiàn)參數(shù)A時(shí)被@Lazy注解修飾時(shí),那么就不會(huì)調(diào)用getBean來獲取對(duì)象,而是創(chuàng)建了一個(gè)代理對(duì)象,所以不會(huì)構(gòu)成真正的循環(huán)依賴,不會(huì)拋出BeanCurrentlyInCreationException異常。

/**
 * 處理懶加載對(duì)象
 * 懶加載返回的又是一個(gè)代理對(duì)象,不會(huì)真正的調(diào)用getBean,所以如果構(gòu)造方法依賴中有循環(huán)依賴,那么不會(huì)報(bào)錯(cuò)
 * @see org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String)
 */
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
		descriptor, requestingBeanName);
if (result == null) {
	// 調(diào)用beanFactory.getBean(beanName)從容器中獲取依賴對(duì)象
	result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;

在這里插入圖片描述

setter prototype循環(huán)依賴

對(duì)于prototype作用域bean,Spring容器無法完成依賴注入,因?yàn)镾pring容器不進(jìn)行緩存"prototype"作用域的bean,因此無法提前暴露一個(gè)創(chuàng)建中的bean。

使用

@Data
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBeanA {
	@Autowired
	private PrototypeBeanB prototypeBeanB;
}
@Data
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBeanB {
	@Autowired
	private PrototypeBeanA prototypeBeanA;
}
@Test
public void test3() {
	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
	applicationContext.register(PrototypeBeanA.class);
	applicationContext.register(PrototypeBeanB.class);
	applicationContext.refresh();
	applicationContext.getBean(PrototypeBeanA.class); // 此時(shí)必須要獲取Spring管理的實(shí)例,因?yàn)楝F(xiàn)在scope="prototype" 只有請求獲取的時(shí)候才會(huì)實(shí)例化對(duì)象
}

運(yùn)行結(jié)果如下:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'prototypeBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:269)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1322)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:668)
 ... 89 more

源碼分析

在這里插入圖片描述

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

... ...
// 判斷是否在當(dāng)前創(chuàng)建Bean池中
if (isPrototypeCurrentlyInCreation(beanName)) {
	throw new BeanCurrentlyInCreationException(beanName);
}
... ...

異常就是在上面的代碼中拋出來的,那么beanName是什么時(shí)候添加至當(dāng)前創(chuàng)建Bean池中的呢?

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	// prototype類型的bean的實(shí)例化
	Object prototypeInstance = null;
	try {
		beforePrototypeCreation(beanName);
		prototypeInstance = createBean(beanName, mbd, args);
	}
	finally {
		afterPrototypeCreation(beanName);
	}
	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

org.springframework.beans.factory.support.AbstractBeanFactory#beforePrototypeCreation

protected void beforePrototypeCreation(String beanName) {
	// ThreadLocal
	Object curVal = this.prototypesCurrentlyInCreation.get();
	if (curVal == null) {
		this.prototypesCurrentlyInCreation.set(beanName);
	}
	else if (curVal instanceof String) {
		Set<String> beanNameSet = new HashSet<>(2);
		beanNameSet.add((String) curVal);
		beanNameSet.add(beanName);
		this.prototypesCurrentlyInCreation.set(beanNameSet);
	}
	else {
		Set<String> beanNameSet = (Set<String>) curVal;
		beanNameSet.add(beanName);
	}
}

其根本原因就是Spring容器不會(huì)對(duì)prototype類型的bean進(jìn)行緩存,因此無法提前利用三級(jí)緩存暴露一個(gè)代理對(duì)象。

循環(huán)依賴開關(guān)

可以通過allowCircularReferences來禁止循環(huán)依賴,這樣的話,singleton bean的setter循環(huán)依賴也會(huì)報(bào)錯(cuò)。

applicationContext.setAllowCircularReferences(false);

二級(jí)緩存可行?

緩存 說明
singletonObjects 第一級(jí)緩存,存放可用的成品Bean。
earlySingletonObjects 第二級(jí)緩存,存放半成品的Bean,半成品的Bean是已創(chuàng)建對(duì)象,但是未注入屬性和初始化,用以解決循環(huán)依賴。
singletonFactories 第三級(jí)緩存,存的是Bean工廠對(duì)象,用來生成半成品的Bean并放入到二級(jí)緩存中,用以解決循環(huán)依賴。

理論上二級(jí)緩存時(shí)可行的,只需要將三級(jí)緩存中BeanFactory創(chuàng)建的對(duì)象提前放入二級(jí)緩存中,這樣三級(jí)緩存就可以移除了。

那么spring中為什么還要使用三級(jí)緩存呢?如果要使用二級(jí)緩存解決循環(huán)依賴,意味著所有Bean在實(shí)例化后就要完成AOP代理,這樣違背了Spring設(shè)計(jì)的原則,Spring在設(shè)計(jì)之初就是通過AnnotationAwareAspectJAutoProxyCreator這個(gè)后置處理器來在Bean生命周期的最后一步來完成AOP代理,而不是在實(shí)例化后就立馬進(jìn)行AOP代理。

到此這篇關(guān)于spring如何快速穩(wěn)定解決循環(huán)依賴問題的文章就介紹到這了,更多相關(guān)spring循環(huán)依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java程序員常犯的五個(gè)錯(cuò)誤

    Java程序員常犯的五個(gè)錯(cuò)誤

    這篇文章總結(jié)以前經(jīng)驗(yàn)針對(duì)java編程的一些習(xí)慣,給出一些關(guān)于java編程的建議: 當(dāng)你開始成為一個(gè)程序員的時(shí)候,在編程的時(shí)候很容易陷入下面所述的一些壞習(xí)慣,下面把Java程序員常犯的五個(gè)錯(cuò)誤整理如下,需要的朋友可以參考下
    2015-07-07
  • java中構(gòu)造方法和普通方法的區(qū)別說明

    java中構(gòu)造方法和普通方法的區(qū)別說明

    這篇文章主要介紹了java中構(gòu)造方法和普通方法的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Java實(shí)現(xiàn)員工信息管理系統(tǒng)

    Java實(shí)現(xiàn)員工信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)員工信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 解析Java中的默認(rèn)方法

    解析Java中的默認(rèn)方法

    這篇文章主要介紹了Java中的默認(rèn)方法,包括繼承和調(diào)用等Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-07-07
  • springboot接收前端參數(shù)的四種方式圖文詳解

    springboot接收前端參數(shù)的四種方式圖文詳解

    Spring Boot可以通過多種方式接收前端傳遞的數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于springboot接收前端參數(shù)的四種方式,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • java打印指定年月份的日歷

    java打印指定年月份的日歷

    這篇文章主要為大家詳細(xì)介紹了java打印指定年、指定月份的日歷,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • 一文詳細(xì)springboot實(shí)現(xiàn)MySQL數(shù)據(jù)庫的整合步驟

    一文詳細(xì)springboot實(shí)現(xiàn)MySQL數(shù)據(jù)庫的整合步驟

    Spring Boot可以很方便地與MySQL數(shù)據(jù)庫進(jìn)行整合,下面這篇文章主要給大家介紹了關(guān)于springboot實(shí)現(xiàn)MySQL數(shù)據(jù)庫的整合步驟,文中通過圖文以及代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • SpringBoot之@ConditionalOnProperty注解使用方法

    SpringBoot之@ConditionalOnProperty注解使用方法

    在平時(shí)業(yè)務(wù)中,我們需要在配置文件中配置某個(gè)屬性來決定是否需要將某些類進(jìn)行注入,讓Spring進(jìn)行管理,而@ConditionalOnProperty能夠?qū)崿F(xiàn)該功能,文中有詳細(xì)的代碼示例,需要的朋友可以參考下
    2023-05-05
  • Java連接MongoDB的常用方法詳解

    Java連接MongoDB的常用方法詳解

    這篇文章主要為大家詳細(xì)介紹一下Java語言連接MongoDB的常用方法以及實(shí)現(xiàn)增刪改查功能的示例代碼,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-07-07
  • springboot整合shardingsphere和seata實(shí)現(xiàn)分布式事務(wù)的實(shí)踐

    springboot整合shardingsphere和seata實(shí)現(xiàn)分布式事務(wù)的實(shí)踐

    本文主要介紹了springboot整合shardingsphere和seata實(shí)現(xiàn)分布式事務(wù)的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07

最新評(píng)論