简单了解三级缓存解决循环依赖

本文详细解释了Spring框架中遇到循环依赖时的属性赋值机制,强调了通过构造器和set()方法的区别,以及一级、二级和三级缓存的作用。特别指出,对于多例bean和代理bean,三级缓存的特殊处理是关键。

什么是循环依赖问题?

Spring框架有个特点就是大量使用缓存,当Spring需要某个bean对象的时候,它不会直接创建这个bean,而是会先从缓存里面找,如果缓存中没有才会创建这个bean对象,比如Person对象有个Car属性,当Person对象属性赋值的时候就会去容器(缓存)里面拿,那么如果Car对象又有个Person属性,就会不断地重复这个过程,Person获取Car,Car又获取Person,最终方法无限递归造成栈溢出

有什么办法进行属性赋值?

在Spring可以通过构造器或者set()方法进行属性赋值,又分为byName(如@Resource)或者byType(如@Autowired)。

Spring能够解决的只能解决单例beanset()方法注入的循环依赖。

单例bean和多例bean

Spring是用了三级缓存存放提前暴露的半成品bean

而对于多例bean,没有使用缓存所以当Spring遇到多例bean的循环依赖将会直接抛出异常

set()方法和构造器

set()方法是属性赋值使用的办法,配合反射实现

构造器在对象实例化的时候被调用,将会调用getBean属性赋值,但是这里赋值的时候不会加载三级缓存,就会报错

三级缓存解决循环依赖

有哪些缓存?

 // 一级缓存,beanName -> 实例化并且初始化的成品
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 ​
 // 二级缓存,beanName -> 实例化但是未初始化的成品
 private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
 ​
 // 三级缓存,beanName -> ObjectFactory
 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

缓存加入时机

一级缓存

在singletonObject = singletonFactory.getObject();创建完完整的单例bean实例之后会把这个单例bean加入到一级缓存。

二级缓存

this.earlySingletonObjects.put(beanName, singletonObject);把未完全初始化的bean从三级缓存提升到二级缓存。

三级缓存

在初始化之前,调用addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));把bean提前暴露到第三级缓存,防止循环依赖。

总结

其实本质上只有二级缓存,二级缓存提前暴露未完全初始化的单例bean,一级缓存是完整的单例bean,我们用二级缓存给属性赋值,而一级缓存来获取bean,如果有代理bean,那么就会造成beanA和b.属性beanA不同,那第三级缓存实际上是为了代理bean而存在的,我们需要通过第三层缓存SingletonFactory来生成代理对象,注意这里缓存是不能被替换的,只能够新增,所以采用多一级缓存才能对代理bean有效。

Spring通过三级缓存解决循环依赖的问题,但对象作用范围为Prototype或采用构造注入方式时无法解决,只有set注入可以解决,且在Spring Boot 3.0以及Spring framework 6.0之后,默认关闭了对循环依赖的支持[^1][^4]。 ### 原理 在Spring中,循环依赖指的是多个bean之间相互依赖形成一个循环。而三级缓存的设计正是为了解决这种循环依赖问题。一级缓存`singletonObjects`用于存放完全初始化好的单例bean;二级缓存`earlySingletonObjects`在出现循环依赖时,提前保存bean对应的不完整对象,以打破循环依赖,避免出现不同的单例对象,破坏单例条件;三级缓存`singletonFactories`存放的是创建bean的工厂,在需要时通过工厂创建bean对象[^3]。 ### 方法 当Spring容器创建bean时,会按以下步骤使用三级缓存解决循环依赖: 1. **创建bean实例**:开始创建一个bean时,先将其对应的`ObjectFactory`放入三级缓存`singletonFactories`中。 2. **属性注入**:在属性注入过程中,如果发现该bean依赖另一个正在创建的bean,会先从一级缓存`singletonObjects`中查找,若未找到,则从二级缓存`earlySingletonObjects`中查找,若还未找到,则从三级缓存`singletonFactories`中获取对应的`ObjectFactory`,并通过它创建一个早期的bean实例,放入二级缓存`earlySingletonObjects`中,同时从三级缓存`singletonFactories`中移除。 3. **完成创建**:当bean创建完成并初始化后,将其从二级缓存`earlySingletonObjects`移除,放入一级缓存`singletonObjects`中。 ```java // 伪代码示例,展示三级缓存的基本结构 import java.util.HashMap; import java.util.Map; public class SpringCacheExample { // 一级缓存:存放完全初始化好的单例bean private Map<String, Object> singletonObjects = new HashMap<>(); // 二级缓存:存放早期暴露的bean private Map<String, Object> earlySingletonObjects = new HashMap<>(); // 三级缓存:存放创建bean的工厂 private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(); // 省略其他创建和获取bean的方法 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值