【Spring面试题】循环依赖如何解决?

循环依赖是指两个类互相依赖对方的属性,Spring针对setter注入的单例Bean提供了循环依赖解决方案,通过三级缓存机制来处理。构造器注入的循环依赖Spring无法解决,但可以通过setter注入、@Lazy、@PostConstruct以及实现特定接口来规避。文章深入探讨了Spring如何处理这两种情况的循环依赖,并解释了为何选择三级缓存而不是二级缓存。
摘要由CSDN通过智能技术生成

1.什么是循环依赖

循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环。

2.循环依赖的两种情况及其解决方案

2.1 构造器的循环依赖

这种依赖 spring 本身是处理不了的

在这种情况下 Spring 将在加载上下文时引发 BeanCurrentlyInCreationException 异常,因为 Spring 无法决定应该先创建哪个 bean,因为它们彼此相互依赖。

当我们使用构造方法进行注入时,才会遇到这种情况,因为它在上下文加载时就被要求注入。setter 和 field 方法注入则不会发生循环依赖,因为它们不会在创建 Bean 时就注入依赖,而是在被需要时才注入。

解决方案:

  • 采用 setter 和 field 方法注入
  • 使用懒加载 @Lazy
  • 使用 @PostConstruct
  • 实现 ApplicationContextAware 和 InitializingBean 接口

2.2 单例模式下的 setter 循环依赖

Spring 解决循环依赖的机制是根据 Spring 框架内定义的三级缓存来实现的,三级缓存解决了 Bean 之间的循环依赖问题。

从源码中来看看 Spring 中 Bean 工厂是怎么获取 Bean 的(AbstractBeanFactory中):

一级一级向下寻找,可以找到 DefaultSingletonBeanRegistry 类中的三级缓存,其实就是三个 Map 集合类:

  • 第一级缓存【singletonObjects】:里面存放的是实例化好和完成初始化的单例对象
  • 第二级缓存【earlySingletonObjects】:里面存放的是提前曝光的单例对象,这个 Bean 实例化了,但是还没有初始化
  • 第三级缓存【singletonFactories】:里面存放的是要被实例化的对象的对象工厂。

所以当一个 Bean 调用构造函数进行实例化后,即使这时候属性还未填充,依然可以通过三级缓存向外暴露依赖的引用值(所以循环依赖问题的解决也是基于 Java 的引用传递),这也说明了另外一点,基于构造器的注入,如果有循环依赖,Spring是不能够解决的。

还有一点,Spring 默认的 Bean Scope 是单例的,而三级缓存中都包含 singleton,可见是对于单例 Bean 之间的循环依赖的解决。

其实二级缓存也是可以解决循环依赖的。为什么 Spring 不选择二级缓存?

如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,而 Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。所以,Spring 选择了三级缓存。但是因为循环依赖的出现,导致了 Spring 不得不提前去创建代理,因为如果不提前创建代理对象,那么注入的就是原始对象,这样就会产生错误。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的架狗师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值