spring循环依赖解决方案浅显易懂版

本文介绍Spring框架中如何解决单例模式下的循环依赖问题,通过三级缓存机制实现bean的提前曝光,有效避免了循环依赖导致的错误。同时,讨论了构造器注入和原型作用域下循环依赖无法解决的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spring循环依赖

  • 关于Spring bean的创建,其本质上还是一个对象的创建,一个完整的对象包含两部分:当前对象实例化和对象属性的实例化

  • 在Spring中,对象的实例化是通过反射实现的,而对象的属性则是在对象实例化之后通过一定的方式设置的。

  • 那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存

解决办法 三级缓存

  • singletonObjects:一级缓存:用于存放完全初始化好的bean 成品
  • earlySingletonObjects:二级缓存:存放原始bean对象,尚未填充属性,用于解决循环依赖
  • singletonFactories:三级缓存:用于存放bean工厂对象的,用于解决循环依赖
    bean 的获取过程:先从一级获取,失败再从二级、三级里面获取

如果作用域是原型,即scope=“prototype”,也会报错,为什么呢?

  • 首先我们要知道循环依赖只发生在作用域是单例的场景里面,

  • 因为scope="prototype"时,这时候对应的Bean是在使用的时候才会被创建出来,

  • 因为Spring容器不进行缓存“prototype”作用域的bean,因此无法提前暴露一个创建中的bean

(singleton一般是用于无状态的bean,prototype一般用于有状态的bean)

具体步骤

检测循环依赖的过程如下:

  • A 创建过程中需要 B,于是 A 将自己放到三级缓里面 ,去实例化 B

  • B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了!

    • 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
    • B 顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
  • 然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成创建,并将自己放到一级缓存里面
    如此一来便解决了循环依赖的问题

不会二级缓存

  • 是给用户提供接口扩展的,所以采用了三级缓存。

为什么构造器注入不能解决循环依赖问题

  • 因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

  • instantiate(实例化)其实就是理解成new一个对象的过程,而new的时候肯定要执行构造方法,所以猜想对于应该是A在instantiate(实例化)时,进行B的初始化。

  • 因为A中构造器注入了B,那么A在关键的方法addSingletonFactory()之前就去初始化了B,导致三级缓存中根本没有A,所以会发生死循环,Spring发现之后就抛出异常了。

总结

  • Spring对于构造器注入和使用prototype作用域的setter构成的循环依赖是无法解决的,

  • 解决循环依赖前提条件是Bean单例模式下非构造函数的循环依赖,而且要求该对象没有被代理过。

  • Spring通过“提前曝光”机制,配合Java的对象引用原理,解决了某些情况下的循环依赖问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值