问题:
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:
如何理解“依赖”呢,在Spring中有:
- 构造器循环依赖
- field属性注入循环依赖
直接上代码:
构造器循环依赖
@Service public class A { public A(B b) { } } @Service public class B { public B(C c) { } } @Service public class C { public C(A a) { } }
结果:项目启动失败,发现了一个cycle
2.field属性注入循环依赖
@Service public class A1 { @Autowired private B1 b1; } @Service public class B1 { @Autowired public C1 c1; } @Service public class C1 { @Autowired public A1 a1; }
结果:项目启动成功
3.field属性注入循环依赖(prototype)
@Service @Scope("prototype") public class A1 { @Autowired private B1 b1; } @Service @Scope("prototype") public class B1 { @Autowired public C1 c1; } @Service @Scope("prototype") public class C1 { @Autowired public A1 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 { return createBean(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); throw ex; } });
进入getSingleton中,spirng会记录当前beanA正在创建中
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(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方法
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; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }
可以看到这个方法中,除了对后处理器的调用,没有进行任何动作,而是直接返回了我们参数传入的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, boolean allowEarlyReference) { 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); } } } } return singletonObject; }
出处:
https://cloud.tencent.com/developer/article/1455953
https://www.jianshu.com/p/8bb67ca11831