下面这种状况比较常见,A中注入了属性B,B中注入了A属性。java
@Component public class A { @Autowired private B b; //在A中注入B } @Component public class B { @Autowired private A a; //在B中注入A }
还有一种极限状况,A中注入属性A。spring
@Component public class A { @Autowired private A a; }
1、出现循环依赖的Bean必须是单例,原型不行。缓存
第一点很好理解,也很好验证,由于原型的Bean,每次获取的时候都会建立一个新的,那么问题来了,假设在初始化A的时候,须要注入原型的B,接着新建一个A,又新建B……无穷尽。若是真是这样,那还得了。所以,原型状况下Spring没法解决循环依赖,会报错:并发
aused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
app
2、不全是构造器注入的方式。ide
依赖状况 | 依赖注入方式 | 循环依赖是否被解决 |
---|---|---|
AB相互依赖(循环依赖) | 均采用setter方法注入 | 是 |
AB相互依赖(循环依赖) | 均采用构造器注入 | 否 |
AB相互依赖(循环依赖) | A中注入B的方式为setter方法,B中注入A的方式为构造器 | 是 |
AB相互依赖(循环依赖) | B中注入A的方式为setter方法,A中注入B的方式为构造器 | 否 |
在讨论Spring如何解决循环依赖以前,咱们须要清除SpringBean的建立流程,以前的那篇文章讨论了容器的启动销毁与对象完整的生命周期,这里将其中涉及循环依赖的主要部分再作一个说明:函数
createBeanInstance
:实例化,其实也就是调用对象的构造方法或者工厂方法实例化对象populateBean
:填充属性,这一步主要是对bean的依赖属性进行注入(@Autowired
)initializeBean
:回调执行initMethod
、InitializingBean
等方法能够想到,对于单例的bean,在createBeanInstance的时候,应该没啥问题,循环依赖的问题应该发生在第二步属性注入的时候,而这时后这个实例的状态,正好处于:已经实例化,还未初始化的中间状态。这一点很是关键!!!!post
在DefaultSingletonBeanRegistry
类中,维护了三个注释以Cache of
开头的Map,经过检讨能够注意到,三级缓存与前两级缓存不太同样,Map中维护的值是ObjectFactory类型。学习
//单例缓存池 beanName - instance 一级缓存 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //bean的早期引用, bean name to bean instance 二级缓存 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); //单例工厂 beanName - ObjectFactory 三级缓存 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
singletonObjects
:一级缓存,一个单例bean【实例化+初始化】都完成以后,将会加入一级缓存,也就是咱们俗称的单例池。earlySingletonObjects
:二级缓存,用于存放【实例化完成,还没初始化】的实例,提早暴露,用于解决循环依赖问题。singletonFactories
:三级缓存,存放单例对象工厂ObjectFactory,与二级缓存不一样的是,它能够应对产生代理对象。@FunctionalInterface //函数式接口 public interface ObjectFactory<T> { T getObject() throws BeansException; }
还有几个比较重要的集合:this
//bean被建立完成以后,注册 private final Set<String> registeredSingletons = new LinkedHashSet<>(256); //正在建立过程当中的bean待的地儿,bean在开始建立的时候放入,知道建立完成将其移除 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
AbstractBeanFactory.doGetBean中将会出现两个重载的getSingleton方法:
protected <T> T doGetBean(...){ Object sharedInstance = getSingleton(beanName);// // // typeCheckOnly 为 false,将当前 beanName 放入一个 alreadyCreated 的 Set 集合中。表示已经建立过一次 if (!typeCheckOnly) { markBeanAsCreated(beanName); } // 这个getSingleton方法很是关键。 //一、标注a正在建立中~ //二、调用singletonObject = singletonFactory.getObject();(实际上调用的是createBean()方法) 所以这一步最为关键 //三、标注此时实例已经建立完成 //四、执行addSingleton()添加进一级缓存, //同时移除二级和三级缓存,还有注册 sharedInstance = getSingleton(beanName, () -> { ... return createBean(beanName, mbd, args); }); }
protected Object getSingleton(String beanName, boolean allowEarlyReference)
咱们的流程进行到AbstractBeanFactory#doGetBean的时候,会执行Object sharedInstance = getSingleton(beanName);
,接着会执行getSingleton(beanName,true)
,一路跟进去,最终会进到DefaultSingletonBeanRegistry 的getSingleton方法,这个方法十分重要,咱们具体看一看:
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { //先从一级缓存中获取,获取到,就直接返回 Object singletonObject = this.singletonObjects.get(beanName //若是一级缓存获取不到,且这个获取的这个bean正在建立中,就从二级缓存中获取 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //从二级缓存中获取 singletonObject = this.earlySingletonObjects.get(beanName); //仍是获取不到,而且allowEarlyReference为true if (singletonObject == null && allowEarlyReference) { //从三级缓存中获取 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //循环依赖第二次进入的时候,发现A 的三级缓存,因而能够获取到A 的实例, singletonObject = singletonFactory.getObject(); //获取到以后将其置入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); //原先的那个就从三级缓存中移除 this.singletonFactories.remove(beanName); } } } } return singletonObject; }
另一个Singleton重载的方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { //将beanName放入到singletonsCurrentlyInCreation这个集合中,标志着这个单例Bean正在建立 beforeSingletonCreation(beanName); boolean newSingleton = false; // 传入的lambda在这里会被执行,调用createBean方法建立一个Bean后返回 singletonObject = singletonFactory.getObject(); newSingleton = true; singletonObject = this.singletonObjects.get(beanName); } // 建立完成后将对应的beanName从singletonsCurrentlyInCreation移除 afterSingletonCreation(beanName); } if (newSingleton) { //加入一级缓存 addSingleton(beanName, singletonObject); } } return singletonObject; }
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { //加入一级缓存 this.singletonObjects.put(beanName, singletonObject); //三级缓存移除 this.singletonFactories.remove(beanName); //二级缓存移除 this.earlySingletonObjects.remove(beanName); //注册一下 this.registeredSingletons.add(beanName); } }
AbstractAutowireCapableBeanFactory#doCreateBean
在对象实例化完成,初始化以前进行:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. 实例化 BeanWrapper instanceWrapper = null; // 调用构造器或工厂方法 实例化 instanceWrapper = createBeanInstance(beanName, mbd, args); ////咱们一般说的bean实例,bean的原始对象,并无进行初始化的对象 A{ b:null} Object bean = instanceWrapper.getWrappedInstance(); //表示是否提早暴露原始对象的引用,对于单例的bean,通常来讲为true, 能够经过allowCircularReferences关闭循环引用解决循环依赖问题 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName)); //是否容许单例提早暴露 if (earlySingletonExposure) { //调用这个方法,将一个ObjectFactory放进三级缓存,二级缓存会对应删除 //getEarlyBeanReference方法: 一、若是有SmartInstantiationAwareBeanPostProcessor,调用他的getEarlyBeanReference方法,二、若是没有,则不变仍是,exposedObject //这里也是AOP的实现之处,AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));//在bean实例化后,属性注入以前,Spring将bean包装成一个工厂添加进三级缓存中 } //此时bean已经实例化完成, 开始准备初始化 // bean为原始对象 Object exposedObject = bean; try { //负责属性的装配(如依赖注入),遇到循环依赖的状况,会在内部getBean("b")->getSingleton(b) populateBean(beanName, mbd, instanceWrapper); //处理bean初始化完成后的各类回调这里有可能返回一个代理对象 exposedObject = initializeBean(beanName, exposedObject, mbd); } //若是bean容许被早期暴露,进入代码 if (earlySingletonExposure) { //第二参数为false表示不会从三级缓存中在检查,最多从二级缓存中找,其实二级缓存就够了,其实以前getSingleton的时候,已经触发了A 的ObjectFactory.getObject(),A实例已经放入二级缓存中 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //若是没有代理,进入这个分支 if (exposedObject == bean) { exposedObject = earlySingletonReference; / } }
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { //若是一级缓存中还没有存在 if (!this.singletonObjects.containsKey(beanName)) { //添加到三级缓存中 this.singletonFactories.put(beanName, singletonFactory); //从二级缓存中移除 this.earlySingletonObjects.remove(beanName); //注册一下 this.registeredSingletons.add(beanName); } } }
前面谈到了这个方法,尚未细说:
//是否容许单例提早暴露 if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
它实际上就是调用了后置处理器的getEarlyBeanReference
,而真正实现了这个方法的后置处理器只有AbstractAutoProxyCreator
,与Aop相关,也就是说,在不考虑Aop的状况下,这个方法压根就和没调用似的。这里咱们也能更加明确,三级缓存出现很大程度上也是为了更好处理代理对象。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { //调用后值处理器的getEarlyBeanReference exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject; }
咱们能够跟进去看一看:
//AbstractAutoProxyCreator @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); //若是须要的话,返回一个代理对象 return wrapIfNecessary(bean, beanName, cacheKey); }
那么若是考虑可能会存在代理对象出现,这时三级缓存中存在的就是这个代理对象,而且以后经过getSingleton从三级缓存中取出,放入二级缓存中的也是这个对象。
本质其实就是 让A注入B,B注入A ,B先注入的是一个还没初始化就提早用的A 的引用。【这里不考虑AOP】
以开头的A,B为例,假设他们都使用属性字段注入:
A首先getBean,试图获取容器中单例A,第一次容器中还不存在,因而就须要开始建立A。
一顿操做,落点:A此时已经被实例化完成,可是尚未初始化,紧接着将A与一个ObjectFactory存入三级缓存 。若是A被AOP代理,经过这个工厂获取到的就是A代理后的对象,若是没有代理,工厂最后获取到的就是A 的实例化对象。
初始化A,意为A的属性赋值,这时发现B须要注入,因而getBean,来一遍相同的步骤。
一顿操做,落点:B此时已经被实例化完成,可是尚未初始化,紧接着将B与一个ObjectFactory存入三级缓存 。
初始化B,发现须要注入A,因而getBean("a"),此时它在三级缓存中找到了A与ObjectFactory<?> singletonFactory
,经过singletonFactory.getObject();
获得A的引用。并将其存入二级缓存,且从三级缓存移除 。
B注入从对象工厂得到的A的引用,此时B已经初始化完成【表明已经注入A成功,实际上是拥有了A的引用】,将B加入到一级缓存,并将B在二级缓存、三级缓存中的玩意清除,返回。
刚刚是A初始化到一半切出来开始实例化B的,那么接下来也应该返回到A的初始化流程中去。
显然B都已经初始化完毕了,A固然也顺利地初始化成功了,一样,也将A加入一级缓存中,并将A在二级缓存、三级缓存中清除。
至此,Spring解决循环依赖结束,A与B都已实例化+初始化完成,并存入一级缓存,且二级缓存、三级缓存中已经没有了A和B。
固然了,这个过程实际上是在实例化A的时候,把B一并实例化了,因而在遍历BeanNames实例化B的时候,就不须要进行这么复杂的操做了,由于一级缓存中已经存在B了。
缘由在于,Spring解决循环依赖实际上是在Bean已经实例化但未初始化这个中间状态的时候进行处理的,所以bean的实例化与初始化两个操做必须分开,才有机会存入三级缓存,提早暴露原始对象。
可是若是使用若是A先使用构造器,在注入的时候,他会去找B,B再注入A,可此时A并无暴露,也就失败了。
但若是A先用setter注入,A会先暴露,再注入B,B再注入A的时候,就能够经过三级缓存拿到A了。
显然不能,Spring经过多个缓存达到存储不一样状态的对象:
若是只有一级缓存,并发状况下,可能取到实例化但未初始化的对象。
三级缓存使用的是工厂,而不是引用,缘由在于:https://mp.weixin.qq.com/s/kS0K5P4FdF3v-fiIjGIvvQ
延迟队实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提早生成代理对象,不然只会建立一个工厂并将其放入到三级缓存中,可是不会去经过这个工厂真正建立对象。
答:这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提早生成代理对象,不然只会建立一个工厂并将其放入到三级缓存中,可是不会去经过这个工厂去真正建立对象
咱们思考一种简单的状况,就以单首创建A为例,假设AB之间如今没有依赖关系,可是A被代理了,这个时候当A完成实例化后仍是会进入下面这段代码:
// A是单例的,mbd.isSingleton()条件知足 // allowCircularReferences:这个变量表明是否容许循环依赖,默认是开启的,条件也知足 // isSingletonCurrentlyInCreation:正在在建立A,也知足 // 因此earlySingletonExposure=true boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); // 仍是会进入到这段代码中 if (earlySingletonExposure) { // 仍是会经过三级缓存提早暴露一个工厂对象 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }看到了吧,即便没有循环依赖,也会将其添加到三级缓存中,并且是不得不添加到三级缓存中,由于到目前为止Spring也不能肯定这个Bean有没有跟别的Bean出现循环依赖。
假设咱们在这里直接使用二级缓存的话,那么意味着全部的Bean在这一步都要完成
AOP
代理。这样作有必要吗?不只没有必要,并且违背了Spring在结合
AOP
跟Bean的生命周期的设计!Spring结合AOP
跟Bean的生命周期自己就是经过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization
方法中对初始化后的Bean完成AOP
代理。若是出现了循环依赖,那没有办法,只有给Bean先建立代理,可是没有出现循环依赖的状况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。
图片来源:http://www.javashuo.com/article/p-smkruniq-nt.html
Spring经过三级缓存解决了循环依赖:
singletonObjects
:一级缓存,一个单例bean【实例化+初始化】都完成以后,将会加入一级缓存,也就是咱们俗称的单例池。earlySingletonObjects
:二级缓存,用于存放【实例化完成,还没初始化】的实例,提早暴露,用于解决循环依赖问题。singletonFactories
:三级缓存,存放单例对象工厂ObjectFactory,与二级缓存不一样的是,它能够应对产生代理对象。Spring不可以解决先用构造器注入状况的循环依赖,缘由在于Spring解决循环依赖的关键在于bean实例实例化完成,初始化以前的状态,将其加入三级缓存,提早暴露bean。
最后,循环依赖应当在编码的时候就考虑去尽可能避免,若是避免不了,那就尽可能不要使用构造器注入,可使用字段注入。
有点晕了,原本想简单地学习一下,没想到一套接着一套,头晕眼花,仍是代码看的太少了,继续努力。感受有点乱,若是有说的不对的地方,还望评论区指点一二!!抱拳!!!
参考资料: