循环依赖一直都是spring一个老生常谈的问题,为了加深影响特此整理了一下spring是如何解决循环依赖的。
什么是循环依赖?
java中循环依赖分为两类,一种是通过构造器传入实例化参数的时候两个对象相互依赖,另一种是则是通过属性值产生的循环依赖。如下图分别展示了这种情况。
对于第一种情况是没有什么解决办法的,因为JVM虚拟机在对类进行实例化的时候需要先实例化构造参数,而由于循环引用这个参数无法提前实例化,所以能抛出异常。第二种是通过属性注入的方式,A和B相互依赖形成了一个闭环这种是可以解决的。
spring是如何解决循环依赖?
我们直接从初始化bean的源码的地方入手。找到AbstractBeanFactory类中的doGetBean方法查看该方法下的getSingleton 方法如下图:
先看第一张图我们可以看到有三个map, 我分别说下这三个map的作用。
singletonObjects:这个map主要是用来存储已经初始化完成bean对象。
earlySingletonObjects:这个map主要是用来存储已经实例化的bean但是还未填充类属性。
singletonFactories:这个map主要是用来存ObjectFactory ( 通过调用getObject方法生成bean)对象。
接着我们来分析第二张图这个方法就是获取单例的bean的。我们来看下该方法是如何来获取的,我们可以看出首先是从singletonObjects获取bean如果为null的话接下来就从earlySingletonObjects来获取bean,若都没有的话然后再从singletonFactories获取。如果不为空就将获取到的bean添加到earlySingletonObjects中去,并从singletonFactories中移除掉ObjectFactory。以上就是获取singletonObject的一个大概的流程。至于为什么是这么获取其实是有原因的我们接着从getSingleton方法往下看:
上图其实就是如果当前的bean若是单例的话就调用getSingleton方法这里运用了java8函数式编程。简单来说这个方法做的就是若singletonObjects没有找到初始化完成的bean就调用了singletonFactory.getObject方法获取我们的实例对象,最后调用addSingleton将初始化完成的bean添加到singletonObjects中去。
我们接着看getObject方法里到底做了啥,我们可以从图中看到里面执行了AbstractAutowireCapableBeanFactory的doCreateBean方法我们直接看图:
从doCreateBean方法中我标示出了两个重要的方法,我们直接先看第二个方法populateBean从方法名我们可以直译过来就是填充bean的属性。我们注意到在执行方法二的时候先执行了方法一,方法一是干嘛的呢其实方法一就是做了最重要的一步就是将bean的早期引用提前暴露到我们上面所说的singletonFactories的中去。这样当我们填充属性的时候如果遇到Spring中的对象属性,则再循环调用getBean方法获取该对象。
所以spring如何解决循环依赖一句话概括就是:Spring通过将实例化后的对象提前暴露给Spring容器中的singletonFactories,解决了循环依赖的问题。
补充:其实我们从文章中仔细分析就会发现其实用两个缓存其实也可以解决循环依赖问题就是在bean的objectFactory暴露到singletonFactories过后直接调用getObject方法将半成品bean放入到earlySingletonObjects。这样也行但是意味着Bean在构造完后就创建代理对象,这样违背了Spring设计原则。Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。