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

本文探讨了Spring中构造器和Setter循环依赖的处理,重点介绍了三级缓存机制如何解决单例Bean的依赖问题,以及其原理和为何选择三级缓存而非二级。同时涵盖了单例模式下通过setter获取Bean的流程和应用场景。
摘要由CSDN通过智能技术生成

目录

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的循环依赖问题:

singletonObjects(一级)

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

earlySingletonObjects(二级)

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

singletonFactories(三级):

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

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

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

4、三级缓存原理

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

  1. 实例化A:并把获取半成品A对象的BeanFactory,放入Map<String, ObjectFactory<?>> 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生成(获取)早期依赖。

6、补充

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

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值