循环依赖其实就是循环引用,很多地方都说需要两个或则两个以上的bean互相持有对方最终形成闭环才是循环依赖,比如a依赖于b,b依赖于c,c又依赖于a。其实一个bean持有自己类型的属性也会产生循环依赖。

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); // 三级缓存

对于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);
	}
}

@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;

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

源码分析

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循环依赖也会报错。


二级缓存可行?

缓存 说明
singletonobjects 第一级缓存,存放可用的成品bean。
earlysingletonobjects 第二级缓存,存放半成品的bean,半成品的bean是已创建对象,但是未注入属性和初始化,用以解决循环依赖。
singletonfactories 第三级缓存,存的是bean工厂对象,用来生成半成品的bean并放入到二级缓存中,用以解决循环依赖。

理论上二级缓存时可行的,只需要将三级缓存中beanfactory创建的对象提前放入二级缓存中,这样三级缓存就可以移除了。

那么spring中为什么还要使用三级缓存呢?如果要使用二级缓存解决循环依赖,意味着所有bean在实例化后就要完成aop代理,这样违背了spring设计的原则,spring在设计之初就是通过annotationawareaspectjautoproxycreator这个后置处理器来在bean生命周期的最后一步来完成aop代理,而不是在实例化后就立马进行aop代理。

前言

到此这篇关于spring如何解决循环依赖问题的文章就介绍到这了,更多相关spring循环依赖内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!