spring可以解决属性注入循环依赖,默认不能解决构造器注入循环依赖。
spring创建对象分两步,①初始化实例对象,②初始化对象属性。
spring循环依赖,最初引用的就是半成品,也就是只初始化示例对象,还没有初始化对象属性。
1. 循环依赖导致系统启动失败的情况
发生循环依赖一般是在构造方法中注入引起循环依赖错误。
1. 循环依赖
一般发生循环依赖也就是构造方法发生依赖,比如:
类A:
packageqz;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;
@Componentpublic classCircularDependencyA {privateCircularDependencyB circB;
@AutowiredpublicCircularDependencyA(CircularDependencyB circB) {this.circB =circB;
}
}
类B:
packageqz;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;
@Componentpublic classCircularDependencyB {privateCircularDependencyA circA;
@AutowiredpublicCircularDependencyB(CircularDependencyA circA) {this.circA =circA;
}
}
主类:
packageqz;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.context.annotation.ComponentScan;importjava.io.IOException;
@ComponentScan("qz")public classMainApp {public static void main(String[] args) throwsInterruptedException, IOException {
AnnotationConfigApplicationContext applicationContext= new AnnotationConfigApplicationContext(MainApp.class);
applicationContext.close();
}
}
结果:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'circularDependencyB' defined in file [E:\xiangmu\springsource2\spring-framework-5.1.x\mytest\build\classes\java\main\qz\CircularDependencyB.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circularDependencyA': Requested bean is currently in creation: Is there an unresolvable circular reference?
2. 解决办法
1. 重新设计,说白了就是构造注入改为属性注入
类A:
packageqz;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;
@Componentpublic classCircularDependencyA {
@AutowiredprivateCircularDependencyB circB;
}
类B:
packageqz;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;
@Componentpublic classCircularDependencyB {
@AutowiredprivateCircularDependencyA circA;
}
2. 使用@Lazy 注解: 只用一方打注解即可
packageqz;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Lazy;importorg.springframework.stereotype.Component;
@Componentpublic classCircularDependencyA {privateCircularDependencyB circB;
@AutowiredpublicCircularDependencyA(@Lazy CircularDependencyB circB) {this.circB =circB;
}
}
3. 使用@PostConstruct
类A
packageqz;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importjavax.annotation.PostConstruct;
@Componentpublic classCircularDependencyA {
@AutowiredprivateCircularDependencyB circB;
@PostConstructpublic voidinit() {
circB.setCircA(this);
}publicCircularDependencyB getCircB() {returncircB;
}
}
类B:
packageqz;importorg.springframework.stereotype.Component;
@Componentpublic classCircularDependencyB {privateCircularDependencyA circA;public voidsetCircA(CircularDependencyA circA) {this.circA =circA;
}
}
4. 实现ApplicationContextAware and InitializingBean 接口
类A:
packageqz;importorg.springframework.beans.BeansException;importorg.springframework.beans.factory.InitializingBean;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importorg.springframework.stereotype.Component;
@Componentpublic class CircularDependencyA implementsApplicationContextAware, InitializingBean {privateCircularDependencyB circB;privateApplicationContext context;publicCircularDependencyB getCircB() {returncircB;
}
@Overridepublic void afterPropertiesSet() throwsException {
circB= context.getBean(CircularDependencyB.class);
}
@Overridepublic void setApplicationContext(final ApplicationContext ctx) throwsBeansException {
context=ctx;
}
}
类B:
packageqz;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;
@Componentpublic classCircularDependencyB {privateCircularDependencyA circA;
@Autowiredpublic voidsetCircA(CircularDependencyA circA) {this.circA =circA;
}
}
2. 属性注入循环依赖Spring解决方式分析
通过之前分析得到,Spring创建Bean是从getBean开始。主要分为两部分。当前对象实例化和对象属性的实例化。对象的实例化通过反射实现,对象的属性是在实例化之后通过注入或者其他方式实现的。
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 源码如下:
protected T doGetBean(
String name, @Nullable Class requiredType, @Nullable Object[] args, booleantypeCheckOnly)throwsBeansException {
String beanName=transformedBeanName(name);
Object bean;
Object sharedInstance=getSingleton(beanName);if (sharedInstance != null && args == null) {if(logger.isTraceEnabled()) {if(isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}else{
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean= getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else{//Fail if we're already creating this bean instance://We're assumably within a circular reference.
if(isPrototypeCurrentlyInCreation(beanName)) {throw newBeanCurrentlyInCreationException(beanName);
}//Check if bean definition exists in this factory.
BeanFactory parentBeanFactory =getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {//Not found -> check parent.
String nameToLookup =originalBeanName(name);if (parentBeanFactory instanceofAbstractBeanFactory) {return((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}else if (args != null) {//Delegation to parent with explicit args.
return(T) parentBeanFactory.getBean(nameToLookup, args);
}else if (requiredType != null) {//No args -> delegate to standard getBean method.
returnparentBeanFactory.getBean(nameToLookup, requiredType);
}else{return(T) parentBeanFactory.getBean(nameToLookup);
}
}if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}try{
RootBeanDefinition mbd=getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);//Guarantee initialization of beans that the current bean depends on.
String[] dependsOn =mbd.getDependsOn();if (dependsOn != null) {for(String dep : dependsOn) {if(isDependent(beanName, dep)) {throw newBeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);try{
getBean(dep);
}catch(NoSuchBeanDefinitionException ex) {throw newBeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}//Create bean instance.
if(mbd.isSingleton()) {
sharedInstance= getSingleton(beanName, () ->{try{returncreateBean(beanName, mbd, args);
}catch(BeansException ex) {//Explicitly remove instance from singleton cache: It might have been put there//eagerly by the creation process, to allow for circular reference resolution.//Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);throwex;
}
});
bean=getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}else if(mbd.isPrototype()) {//It's a prototype -> create a new instance.
Object prototypeInstance = null;try{
beforePrototypeCreation(beanName);
prototypeInstance=createBean(beanName, mbd, args);
}finally{
afterPrototypeCreation(beanName);
}//create bean
bean =getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}else{
String scopeName=mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope= this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}try{
Object scopedInstance= scope.get(beanName, () ->{
beforePrototypeCreation(beanName);try{returncreateBean(beanName, mbd, args);
}finally{
afterPrototypeCreation(beanName);
}
});
bean=getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}catch(IllegalStateException ex) {throw newBeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}catch(BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);throwex;
}
}//Check if required type matches the type of the actual bean instance.
if (requiredType != null && !requiredType.isInstance(bean)) {try{
T convertedBean=getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}returnconvertedBean;
}catch(TypeMismatchException ex) {if(logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType)+ "'", ex);
}throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}return(T) bean;
}
解释:
1. Object sharedInstance = getSingleton(beanName); 先尝试获取对象实例,查看getSingleton(beanName) 源码如下:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
/*** Return the (raw) singleton object registered under the given name.
*
Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
*@parambeanName the name of the bean to look for
*@paramallowEarlyReference whether early references should be created or not
*@returnthe registered singleton object, or {@codenull} if none found*/@Nullableprotected Object getSingleton(String beanName, booleanallowEarlyReference) {//Quick check for existing instance without full singleton lock//尝试从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);//如果一级不存在,则判断当前对象是否处于创建过程中(第一次尝试获取A对象实例之后就会标记为创建中)。
if (singletonObject == null &&isSingletonCurrentlyInCreation(beanName)) {//从二级缓存中查询,获取Bean的早期引用,实例化完成,但未赋值完成的bean
singletonObject = this.earlySingletonObjects.get(beanName);//二级缓存不存在,并且允许创建早期引用
if (singletonObject == null &&allowEarlyReference) {synchronized (this.singletonObjects) {
singletonObject= this.singletonObjects.get(beanName);if (singletonObject == null) {
singletonObject= this.earlySingletonObjects.get(beanName);if (singletonObject == null) {//从三级缓存中查询,实例化完成,属性为装配完成
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {//获取目标对象的实例
singletonObject =singletonFactory.getObject();// //添加到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);//从三级缓存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}returnsingletonObject;
}
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#isSingletonCurrentlyInCreation源码如下:
public booleanisSingletonCurrentlyInCreation(String beanName) {return this.singletonsCurrentlyInCreation.contains(beanName);
}
一二三级缓存全部查询,如果三级缓存存在则将Bean早期引用存放在二级缓存中并移除三级缓存。(升级为二级缓存)。这里看到用到了三个map,如下:
/**Cache of singleton objects: bean name to bean instance.*/
private final Map singletonObjects = new ConcurrentHashMap<>(256);/**Cache of singleton factories: bean name to ObjectFactory.*/
private final Map> singletonFactories = new HashMap<>(16);/**Cache of early singleton objects: bean name to bean instance.*/
private final Map earlySingletonObjects = new ConcurrentHashMap<>(16);
singletonObjects 对应一级缓存。 存放完全实例化属性赋值完成的Bean,可以直接使用
earlySingletonObjects 对应二级缓存,存放早起Bean的引用,尚未属性装配的Bean,也就是半成品
singletonFactories 对应三级缓存,存放实例化完成的Bean工厂。
2. 如下代码是创建Bean且加入相关缓存
if(mbd.isSingleton()) {
sharedInstance= getSingleton(beanName, () ->{try{returncreateBean(beanName, mbd, args);
}catch(BeansException ex) {//Explicitly remove instance from singleton cache: It might have been put there//eagerly by the creation process, to allow for circular reference resolution.//Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);throwex;
}
});
bean=getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
2.1 这段代码实际上用了一个lambda表达式,也就是如果getSingleton 获取不到bean则会创建bean。org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory>)源码如下:
public Object getSingleton(String beanName, ObjectFactory>singletonFactory) {
Assert.notNull(beanName,"Bean name must not be null");synchronized (this.singletonObjects) {
Object singletonObject= this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw newBeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}if(logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if(recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();
}try{
singletonObject=singletonFactory.getObject();
newSingleton= true;
}catch(IllegalStateException ex) {//Has the singleton object implicitly appeared in the meantime ->//if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throwex;
}
}catch(BeanCreationException ex) {if(recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}throwex;
}finally{if(recordSuppressedExceptions) {this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}if(newSingleton) {
addSingleton(beanName, singletonObject);
}
}returnsingletonObject;
}
}
beforeSingletonCreation(beanName); 方法会将创建标记存入Map中,标记着该Bean开始创建。org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation源码如下:
protected voidbeforeSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw newBeanCurrentlyInCreationException(beanName);
}
}
2.2 也就是获取不到Bean调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean方法,方法中委托给org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
protectedObject doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throwsBeanCreationException {//Instantiate the bean.
BeanWrapper instanceWrapper = null;if(mbd.isSingleton()) {
instanceWrapper= this.factoryBeanInstanceCache.remove(beanName);
}if (instanceWrapper == null) {
instanceWrapper=createBeanInstance(beanName, mbd, args);
}
Object bean=instanceWrapper.getWrappedInstance();
Class> beanType =instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {
mbd.resolvedTargetType=beanType;
}//Allow post-processors to modify the merged bean definition.
synchronized(mbd.postProcessingLock) {if (!mbd.postProcessed) {try{
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}catch(Throwable ex) {throw newBeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed= true;
}
}//Eagerly cache singletons to be able to resolve circular references//even when triggered by lifecycle interfaces like BeanFactoryAware.
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));
}//Initialize the bean instance.
Object exposedObject =bean;try{
populateBean(beanName, mbd, instanceWrapper);
exposedObject=initializeBean(beanName, exposedObject, mbd);
}catch(Throwable ex) {if (ex instanceof BeanCreationException &&beanName.equals(((BeanCreationException) ex).getBeanName())) {throw(BeanCreationException) ex;
}else{throw newBeanCreationException(
mbd.getResourceDescription(), beanName,"Initialization of bean failed", ex);
}
}if(earlySingletonExposure) {
Object earlySingletonReference= getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject ==bean) {
exposedObject=earlySingletonReference;
}else if (!this.allowRawInjectionDespiteWrapping &&hasDependentBean(beanName)) {
String[] dependentBeans=getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for(String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}if (!actualDependentBeans.isEmpty()) {throw newBeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans)+
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}//Register bean as disposable.
try{
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}catch(BeanDefinitionValidationException ex) {throw newBeanCreationException(
mbd.getResourceDescription(), beanName,"Invalid destruction signature", ex);
}returnexposedObject;
}
2.3 可以看到 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)) 代码调用添加三级缓存。org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory如下:
/*** Add the given singleton factory for building the specified singleton
* if necessary.
*
To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
*@parambeanName the name of the bean
*@paramsingletonFactory the factory for the singleton object*/
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);
}
}
}
2.4 上面2.2中的 populateBean 方法调用会进行依赖注入等操作,如果属性注入其他bean会循环调用getBean方法
2.5 在2.1的代码块中可以看到addSingleton(beanName, singletonObject); 加入缓存的代码。org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton:
/*** Add the given singleton object to the singleton cache of this factory.
*
To be called for eager registration of singletons.
*@parambeanName the name of the bean
*@paramsingletonObject the singleton object*/
protected voidaddSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {//一级添加
this.singletonObjects.put(beanName, singletonObject);//二级移除
this.singletonFactories.remove(beanName);//三级移除
this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);
}
}
加入缓存之后完成整个bean的创建以及赋值。
也就是二级缓存相当于个过渡。从三级到一级的过渡,放的是一个半成品。
总结下流程相当于:假设A依赖B、B依赖A。
(1)A依次执行doGetBean、查询缓存、createBean创建实例,实例化完成放入三级缓存singletonFactories中,接着执行populateBean方法装配属性,但是发现有一个属性是B的对象。
(2)再次调用doGetBean方法创建B的实例,依次执行doGetBean、查询缓存、createBean创建实例,实例化完成之后放入三级缓存singletonFactories中,执行populateBean装配属性,但是此时发现有一个属性是A对象。
(3)因此再次调用doGetBean创建A的实例,但是执行到getSingleton查询缓存的时候,从三级缓存中查询到了A的实例(早期引用,未完成属性装配),此时移到二级缓存中然后返回,不用执行后续的流程创建A了,那么B就完成了属性装配,此时是一个完整的对象放入到一级缓存singletonObjects中。
(4)B创建完成了,则A自然完成了属性装配,也创建完成放入了一级缓存singletonObjects中。