面试汇总-Spring-如何解决Spring Bean的循环依赖问题

目录

1、构造器循环依赖(无法解决)

2、Setter循环依赖

3、解决方案

3.1、三级缓存

3.2、单例模式下通过setter获取Bean的流程

4、三级缓存原理

5、为什么使用三级缓存而不是二级缓存

6、补充


1、构造器循环依赖(无法解决)

        在调用构造函数的时候就进行成员变量的初始化。

        Spring有一个“当前正在创建Bean池”,每一个正在创建的Bean标识符都会放到该池中。当创建bean的过程中发现自己已经存在“当前正在创建Bean池”中时,就会抛出异常,表示循环依赖。创建完毕后的bean,标识符会从Bean池中移除。

示例:

通过构造器初始化对象时:

  1. 创建A,A依赖于B,所以将A放入“正在创建Bean池中”,先去创建B;
  2. 创建B,B依赖于C,所以将B放入“正在创建Bean池中”,先去创建C;
  3. 创建C,C依赖于A,所以将C放入“正在创建Bean池中”,先去创建A;
  4. 创建A,发现A已经在“正在创建Bean池”中,抛出异常。

2、Setter循环依赖

        在调用构造函数的时候不进行成员变量的初始化,而是先将Bean对象实例化(无参构造函数),然后再设置对象属性。

3、解决方案

3.1、三级缓存

        Spring通过将实例化后的对象(未设置属性值)提前暴露给Spring容器中的singletonFactories,解决了循环依赖的问题

        三级缓存只能解决单例Bean的循环依赖问题,因为"prototype"作用域中,Spring的对象实例是在被调用的时候才会进行,因此无法提前暴露一个创建中的bean。

三级缓存来解决bean的循环依赖问题:

面试汇总-Spring-如何解决Spring Bean的循环依赖问题_第1张图片

singletonObjects(一级)

        单例对象缓存(单例池),存放可用的成品Bean。

earlySingletonObjects(二级)

        存放半成品的Bean,已实例化的对象,但尚未进行属性赋值和初始化操作。

singletonFactories(三级):

        存放Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。

3.2、单例模式下通过setter获取Bean的流程

面试汇总-Spring-如何解决Spring Bean的循环依赖问题_第2张图片

  1. 从单例对象缓存singletonObjects中获取;
  2. 若获取对象为null,且对象正在创建中,则从earlySingletonObjects中获取;
  3. 若获取对象仍为null,且允许从单例工厂缓存中获取,则从singletonFactory中获取;获取成功后,则将该对象放入到earlySingletonObjects缓存中,暴露出来。

4、三级缓存原理

假设A、B对象存在循环依赖:

  1. 实例化A:并把获取半成品A对象的BeanFactory,放入Map> singletonFactories缓存(三级)中(这里并不是直接将Bean放入缓存,而是包装成ObjectFactory对象再放入);
  2. A属性赋值(阻塞):发现A依赖B,去一二三级缓存中找B,找不到,则去创建对象B;
  3. 实例化B:同A,把获取半成品B对象的BeanFactory,放入singletonFactories缓存(三级)中;
  4. B属性赋值:发现B依赖A,从三级缓存中通过singletonFactories得到A,然后把对象A放入earlySingletonObjects缓存(二级)中,并删除三级缓存中的对象A;
  5. B属性赋值成功:对象B创建完成,把B放入一级缓存,删除三级缓存中的对象B;
  6. A属性赋值成功:从一级缓存中得到B,属性赋值成功,删除二级缓存中的A,并将A放入一级缓存。

5、为什么使用三级缓存而不是二级缓存

        创建普通Bean(非代理,无AOP)对象时,二级缓存和三级缓存都可以解决循环依赖。

        Bean的创建流程:实例化->暴露Bean->填充属性->初始化->生成(代理)对象。

假设A、B对象存在循环依赖,且A对象关联AOP:

        如果按以上Bean的创建流程,对象A暴露的是普通对象而非代理对象,所以对象B的属性A注入的也是普通对象而非代理对象。这种情况下,需要在属性填充前,就需要提前暴露A的代理对象。

        三级缓存的bean工厂getObject方式,实际执行的是getEarlyBeanReference,如果对象需要被代理(存在beanPostProcessors -> SmartInstantiationAwareBeanPostProcessor),则提前生成代理对象。

        所以三级缓存的不是对象而是对象工厂,如果不存在AOP,则返回普通实例化对象;如果存在AOP,则提前生成对象的AOP代理对象,并返回。

        出现循环依赖时,才会执行工厂的getObject生成(获取)早期依赖。

面试汇总-Spring-如何解决Spring Bean的循环依赖问题_第3张图片

6、补充

面试汇总-Spring-如何解决Spring Bean的循环依赖问题_第4张图片

以上内容为个人学习汇总,仅供学习参考,如有问题,欢迎在评论区指出,谢谢!

你可能感兴趣的:(面试,Spring,spring,java,后端)