Spring如何解决循环依赖

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

今天面试了同程旅行面试官问到了这个问题所以今天来总结学习一下 Spring是如何解决循环依赖问题

前言

Spring的依赖注入分为 setter注入构造器注入
这里说的解决循环依赖主要指的是单例模式下的setter循环依赖

如果是构造器的循环依赖非单例循环依赖。 Spring都是解决不了的

Spring的生命周期

讲循环依赖的问题需要先说说Spring生命周期这个问题先看看在Spring中一个Bean从创建到销毁经历过哪些过程

Spring中的bean的生命周期主要包含四个阶段实例化Bean --> Bean属性填充 --> 初始化Bean -->销毁Bean

什么是循环依赖

我们定义这样两个类A、BA中有一个属性引用的BB中有一个属性是引用的A

参考上面Bean的生命周期就能得出在实例化A的时候去赋值属性B就去找BB这个时候又需要实例化赋值属性A的时候又去找A。接着A又找B就形成了循环依赖。

@Component
public class A{
  @Autowired
  B b;
}

@Component
public class B{
  @Autowired
  A a;
}

Spring的三级缓存

// 1级缓存存放完全初始化好的Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 2级缓存存放实例化尚未填充属性+代理如果有代理后的单例bean
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

//3级缓存存放bean工厂对象用于解决循环依赖
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

假设只有一级缓存

如果A在实例化过后不进行属性值的填充就把它丢到一级缓存里面行吗其实这样缓存里面的A也不是一个真正的A它缺胳膊少腿用它的时候回报一个空指针异常而且我们的一级缓存规定是完全初始化好的bean才放在一级缓存立面给我们的程序后续进行使用

假设只有二级缓存

我们把一级缓存叫 Map1二级缓存叫Map2

首先还是从实例化A开始我们对A实例化过后还没有进行属性填充的时候就把A对象的引用放在Map2备用然后进行属性填充A去填充B的时候发现B没有实例化于是等B同样实例化过后B也把自己的引用放在Map2中B开始进行属性填充发现Map1中没有A但在Map2中找到了A是自己装填完整。这个时候B把完整的自己放在了Map1随手把Map2中的半成品删除。
再回到A的阶段A中发现Map1中有了B那么A也可以装填完整于是最终A、B都完成了自己的创建。

文字有点儿扭成一坨不容易看懂我画个流程图大家凑合看

在这里插入图片描述
按照以上所述二级缓存已经完美解决了循环依赖的问题为什么Spring还需要引入三级缓存来做呢

主要原因是因为Spring的Aop产生的代理对象

Srping的代理对象产生阶段是在填充属性后才进行原理是通过后置处理器BeanPostProcssor来实现

如果 Spring 选择二级缓存来解决循环依赖的话那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理

所以Spring选择使用三级缓存因为循环依赖的出现导致了 Spring 不得不提前去创建代理因为如果不提前创建代理对象那么注入的就是原始对象这样就会产生错误。

Spring的三级缓存

首先我们还是进行实例化A对象我们将A的ObjectFactory对象放入Map3中同样进行属性填充这个时候发现需要属性B这个时候B还没有创建。
接着去创建B实例化B的过程中一样的我们先将B的ObjectFactory放到Map3中继续执行B的属性填充去寻找A对象。此时Map1和Map2中都没有找到A对象但在Map3中发现了有A的 ObjectFactory对象那么我们就可以通过这个 ObjectFactory对象获取到A的早期对象并且把A早期对象放到Map2中国同时删除Map3中的A我们把Map2中的早期对象A给了B让B对象进行属性装填接着B完整了就把B放入到一级缓存当中再把Map3中B的 ObjectFactory删除。
B创建完成A继续执行b属性的填充就可以拿到Map1中拿到B对象这样A也完整了。
最后把A对象放入到Map1单重删除Map2中的A于是我们的循环依赖的解决了

画一张流程图试着理解一下

在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring