循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:
如何理解“依赖”呢,在Spring中有:
构造器循环依赖
field属性注入循环依赖
直接上代码:
构造器循环依赖
@Servicepublic classA {publicA(B b) { }
}
@Servicepublic classB {publicB(C c) {
}
}
@Servicepublic classC {publicC(A a) { }
}
结果:项目启动失败,发现了一个cycle
2.field属性注入循环依赖
@Servicepublic classA1 {
@AutowiredprivateB1 b1;
}
@Servicepublic classB1 {
@AutowiredpublicC1 c1;
}
@Servicepublic classC1 {
@AutowiredpublicA1 a1;
}
结果:项目启动成功
3.field属性注入循环依赖(prototype)
@Service
@Scope("prototype")public classA1 {
@AutowiredprivateB1 b1;
}
@Service
@Scope("prototype")public classB1 {
@AutowiredpublicC1 c1;
}
@Service
@Scope("prototype")public classC1 {
@AutowiredpublicA1 a1;
}
结果:项目启动失败,发现了一个cycle。
现象总结:同样对于循环依赖的场景,构造器注入和prototype类型的属性注入都会初始化Bean失败。因为@Service默认是单例的,所以单例的属性注入是可以成功的。
Spring如何解决循环依赖
spring中循环依赖有三种情况:
1、构造器注入形成的循环依赖。也就是beanB需要在beanA的构造函数中完成初始化,beanA也需要在beanB的构造函数中完成舒适化,这种情况的结果就是两个bean都不能完成初始化,循环依赖难以解决。
2、setter注入构成的循环依赖。beanA需要在beanB的setter方法中完成初始化,beanB也需要在beanA的setter方法中完成初始化,spring设计的机制主要就是解决这种循环依赖,也是今天下文讨论的重点。
3、prototype作用域bean的循环依赖。这种循环依赖同样无法解决,因为spring不会缓存‘prototype’作用域的bean,而spring中循环依赖的解决正是通过缓存来实现的。
下面主要说明第二种情况中循环依赖的解决方案
步骤一:beanA进行初始化,并且将自己进行初始化的状态记录下来,并提前向外暴露一个单例工程方法,从而使其他bean能引用到该bean(可能读完这一句,您仍然心存疑惑,没关系,继续往下读)
步骤二:beanA中有beanB的依赖,于是开始初始化beanB。
步骤三:初始化beanB的过程中又发现beanB依赖了beanA,于是又进行beanA的初始化,这时发现beanA已经在进行初始化了,程序发现了存在的循环依赖,然后通过步骤一中暴露的单例工程方法拿到beanA的引用(注意,此时的beanA只是完成了构造函数的注入但为完成其他步骤),从而beanB拿到beanA的引用,完成注入,完成了初始化,如此beanB的引用也就可以被beanA拿到,从而beanA也就完成了初始化。
spring进行bean的加载的时候,首先进行bean的初始化(调用构造函数),然后进行属性填充。在这两步中间,spring对bean进行了一次状态的记录,也就是说spring会把指向只完成了构造函数初始化的bean的引用通过一个变量记录下来,明白这一点对之后的源码理解至关重要。
源码角度观看循环依赖的解决步骤
步骤一中首先进行beanA的创建
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;
}
});
进入getSingleton中,spirng会记录当前beanA正在创建中
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw newBeanCurrentlyInCreationException(beanName);
}
并且将注册一个工厂方法来解决循环依赖
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if(earlySingletonExposure) {if(logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
这里: addSingletonFactory(beanName, ()->getEarlyBeanReference(beanName, mbd, bean));
}
主要就是addSingletonFactory,这句就完成了工厂方法的注册,这个方法可以返回一个只完成了构造函数初始化的beanA,也许大家想知道他是如何返回的,我们进入getEarlyBeanReference方法
protectedObject getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject=bean;if (!mbd.isSynthetic() &&hasInstantiationAwareBeanPostProcessors()) {for(BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceofSmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp=(SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject=ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}returnexposedObject;
}
可以看到这个方法中,除了对后处理器的调用,没有进行任何动作,而是直接返回了我们参数传入的bean,那么这个bean是哪来的呢?其实在这之前,spring先调用了beanA的构造函数,并拿到了只完成了构造函数初始化的一个实例,并把他记录了下来。
if(mbd.isSingleton()) {
instanceWrapper= this.factoryBeanInstanceCache.remove(beanName);
}if (instanceWrapper == null) {
instanceWrapper=createBeanInstance(beanName, mbd, args);
}final Object bean = instanceWrapper.getWrappedInstance();
这就是这个bean的由来。然后当我们进行到步骤三的时候,就会检查是否允许循环依赖(即使是Singleton类型的bean也可以通过参数设置,禁止循环依赖),如果允许的话,就会通过这个工厂方法拿到beanA的引用。从而完成beanA和beanB的加载。
protected Object getSingleton(String beanName, booleanallowEarlyReference) {
Object singletonObject= this.singletonObjects.get(beanName);if (singletonObject == null &&isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {
singletonObject= this.earlySingletonObjects.get(beanName);if (singletonObject == null &&allowEarlyReference) {
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {
这里: singletonObject=singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);
}
}
}
}returnsingletonObject;
}
出处:
https://cloud.tencent.com/developer/article/1455953
https://www.jianshu.com/p/8bb67ca11831