亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在spring中解決循環依賴問題

發布時間:2021-03-09 17:22:19 來源:億速云 閱讀:245 作者:Leah 欄目:開發技術

怎么在spring中解決循環依賴問題?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

setter singleton循環依賴

使用

SingleSetterBeanA依賴SingleSetterBeanB,SingleSetterBeanB依賴SingleSetterBeanA。

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

源碼分析

spring是通過三級緩存來解決循環依賴的,那么三級緩存是怎么工作的呢?

三級緩存對應org.springframework.beans.factory.support.DefaultSingletonBeanRegistry類的三個屬性:

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

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

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

怎么在spring中解決循環依賴問題

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

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

// 處理循環依賴,實例化后放入三級緩存
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實例化后放入三級緩存中:

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); // 三級緩存
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

放入三級緩存中的是ObjectFactory類型的lambda表達式:

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創建代理對象
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
	return exposedObject;
}

構造器參數循環依賴

通過構造器注入構成的循環依賴,此依賴是無法解決的,只能拋出BeanCurrentlyInCreationException異常表示循環依賴。

使用

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

上面的代碼運行時會拋出如下異常:

... ...
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容器會將每一個正在創建的Bean標識符放在一個“當前創建Bean池”中,Bean標識符在創建過程中將一直保持在這個池中,因此如果在創建Bean過程中發現自己已經在“當前創建Bean池”里時將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對于創建完畢的Bean將從“當前創建Bean池”中清除掉。

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

怎么在spring中解決循環依賴問題

@Lazy打破循環依賴

在上面的例子中只需要在SingleConstrutorBeanA或者SingleConstrutorBeanB的構造方法上面加上@Lazy注解,就會發現不會拋出異常了,這又是為什么呢?

下面假設在SingleConstrutorBeanA的構造方法上面加了@Lazy注解,在構造B時,發現參數A時被@Lazy注解修飾時,那么就不會調用getBean來獲取對象,而是創建了一個代理對象,所以不會構成真正的循環依賴,不會拋出BeanCurrentlyInCreationException異常。

/**
 * 處理懶加載對象
 * 懶加載返回的又是一個代理對象,不會真正的調用getBean,所以如果構造方法依賴中有循環依賴,那么不會報錯
 * @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) {
	// 調用beanFactory.getBean(beanName)從容器中獲取依賴對象
	result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;

怎么在spring中解決循環依賴問題

setter prototype循環依賴

對于prototype作用域bean,Spring容器無法完成依賴注入,因為Spring容器不進行緩存"prototype"作用域的bean,因此無法提前暴露一個創建中的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); // 此時必須要獲取Spring管理的實例,因為現在scope="prototype" 只有請求獲取的時候才會實例化對象
}

運行結果如下:

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

源碼分析

怎么在spring中解決循環依賴問題

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

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

異常就是在上面的代碼中拋出來的,那么beanName是什么時候添加至當前創建Bean池中的呢?

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

else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	// prototype類型的bean的實例化
	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容器不會對prototype類型的bean進行緩存,因此無法提前利用三級緩存暴露一個代理對象。

循環依賴開關

可以通過allowCircularReferences來禁止循環依賴,這樣的話,singleton bean的setter循環依賴也會報錯。

applicationContext.setAllowCircularReferences(false);

二級緩存可行?

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

理論上二級緩存時可行的,只需要將三級緩存中BeanFactory創建的對象提前放入二級緩存中,這樣三級緩存就可以移除了。

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

關于怎么在spring中解決循環依賴問題問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

凤山县| 寿宁县| 驻马店市| 海林市| 应城市| 安丘市| 南汇区| 宜春市| 邹平县| 鹤岗市| 怀来县| 商洛市| 双桥区| 苏尼特左旗| 华蓥市| 太湖县| 嘉黎县| 合作市| 万荣县| 彭泽县| 七台河市| 南充市| 天门市| 汉川市| 沁阳市| 惠安县| 新民市| 三江| 堆龙德庆县| 江城| 东兰县| 安达市| 定日县| 隆德县| 许昌县| 马边| 宁乡县| 泽普县| 政和县| 丰台区| 上饶县|