源码过程可以参照我的另一博客:Spring 解决循环依赖的过程

解决循环依赖的场景

  • 通过setter注入的单例bean,属性相互引用
  • Prototype的场景不支持循环依赖,会报错

    怎么解决?

    三级缓存,即解决循环依赖的三个Map
    image.png

  • 一级缓存:singletonObjects,是一个ConcurrentHashMap, 也叫单例池,存放已经经历了完整生命周期的对象

  • 二级缓存:earlySingletonObjects, 是一个HashMap,获取早期暴露出来的bean对象。Bean的生命周期未结束,属性还未填充完整。未注入属性和初始化,存放半成品的bean,用来解决循环依赖。
  • 三级缓存:sinfletonFactories, 第三季缓存value是一个工厂,存放可以生成bean的工厂。用来生成半成品的bean存放到二级缓存。

    只有单例的bean会通过三级缓存解决循环依赖的问题,非单例的bean,每次从容器获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会存在三级缓存中。

为什么要包装一层ObjectFactory对象?

如果创建的Bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是Spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择: 1、不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。 2、不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建。 Spring选择了第二种方式。

怎么做到提前曝光又不生成代理对象呢?

Spring就是在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map earlySingletonObjects。

举例

AB两个对象相互依赖,

  • A创建的时候,需要B,于是A把自己放入到三级缓存中,去实例化B
  • B实例化的时候发现需要A,于是B先查一级缓存,发现没有,再查二级缓存,发现也没有,查三级缓存,找到A,然后把三级缓存里的A放入到二级缓存,并删除三级缓存中的A
  • B顺利初始化完毕,将自己放入到一级缓存对象里面,(此时B里面的A依然是创建中的状态),然后回来继续创建A,此时B已经创建结束了,直接从一级缓存中拿到B,并完成创建,然后A把自己放入到一级缓存中去。

    为什么需要三级缓存?

    因为Bean的整个生命周期过程中,有很多的BeanPostProcessor, 如果对Bean进行AOP代理可能产生新的对象。不可能每次执行getObject都产生一个代理对象,所以还需要借助另外一个缓存来保存产生的代理对象。

    那为什么Sping不选择二级缓存方式,而是要额外加一层缓存?

    如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了Spring设计原则。
    Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。
    如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

构造器循环依赖解决

构造器循环依赖解决办法:在构造函数中使用@Lazy注解延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象说明:一种互斥的关系而非层次递进的关系,故称为三个Map而非三级缓存的缘由 完成注入