循环依赖解决的思维


如上图所示,ABean援用了BBean,同时BBean也援用了ABean


Spring在初始化时,会依照beanDefinitionNames的程序(就是Bean的注册程序)顺次初始化所有Bean(对所有的Bean调用一次getBean),而后由BeanFactory进行初始化


初始化Bean有两个要害的流程:

  1. instantiateBean - 创立Bean对象
  2. populateBean - 填充Bean,将Bean中的援用Bean填充至以后Bean


那么问题来了,如果在ABean populateBean 的过程中发现了另一个BBean的援用,此时须要对另一个BBean提前进行初始化操作(getBean),可是BBean初始化的时候也发现了ABean的援用,又会返回对ABean进行初始化,此时就会陷入死循环

抛开Spring的实现细节,这个问题也不难解决,当发现循环援用的时候,只须要提早populateBean的机会就行:

比方发现BBean的援用时,调用getBean(BBean)操作,这个时候B不必实现全副初始化操作,只须要实现第一步instantiateBean 而后就返回BBean的实例

当按beanDefinitionNames的程序初始化到BBean的时候,在对BBean进行populateBean 就能够防止死循环的问题



对于下面的问题解决,关键点是要解决这个未齐全初始化然而有相互援用的对象。最容易想到的就是保护一个中间状态“半初始化”,代表只instantiate并没有进行populate的Bean,这样就能够将实例化和注入“离开”进行,注入时只须要获取已对象的实例即可,对象是否populate实现并不关怀

这个半初始化状态实现也很简略,只须要保护两组对象列表,一个汇合负责存储已instantiate但并没有实现populate半初始化的bean - InCreation,另一个汇合保护加载实现的bean - registered

回到下面那个流程,当ABean执行完instantial后增加到下面的InCreation汇合中,此时populate会调用BBean的初始化,这时Bbean会发现对ABean的援用,先从加载实现的InCreation汇合中获取,如果没有再从InCreation汇合中获取,此时会发现ABean的实例,而后将ABean实例 populate到BBean的实例中,BBean加载实现,增加到registered汇合中;回到ABean的populate过程,此时曾经获取到BBean的返回,间接populate到ABean实例中,ABean加载实现,功败垂成


当然,上述解决方案只是针对属性模式的注入,如果是构造函数的注入就没法解决了,因为连构造方法都没法执行,不能正确的实例化


Spring中的解决思维也是大同小异,此处就不贴代码了,具体能够参考org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

留神:尽管spring对循环援用做了解决,但这种援用关系在程序设计中是不太正当的,应该尽量避免