参考
什么是循环依赖 【框架底层原理|SpringIOC循环依赖底层原理】简单来说,就是A对象依赖了B对象,B对象依赖了A对象。
// A依赖了B
class A{
public B b;
} // B依赖了A
class B{
public A a;
}
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。
A a = new A();
B b = new B();
a.b = b;
b.a = a;
这样A,B就互相依赖了
但是在 Spring 中循环依赖就是一个问题了,因为,在 Spring 中,一个对象并不是简单 new 出来了,而是会经过一系列的 Bean 的生命周期,就是因为 Bean 的生命周期所以才会出现循环依赖问题。当然,在 Spring 中,出现循环依赖的场景很多,有的场景 Spring 自动帮我们解决了,而有的场景则需要程序员来解决。
要明白Spring中的循环依赖,得先明白Spring中Bean的生命周期。
IOC容器加载过程 相当于所有单例Bean的创建
- 当new 一个 applicationContext时,执行容器加载,会从配置中(< bean > @Component @Bean)读取定义信息,加载成一个个的BeanDefination对象,存放在一个BeanDefinationMap中
- 在BeanFactory(负责创建Bean,简单工厂设计模式)中循环创建所有BD,通过getBean方法生产Bean,创建步骤如下
- 首先去Bean容器(一级缓存 Map)中找是否已经存在Bean对象,找到则直接返回
- 没有找到则开始创建Bean:
- 实例化:拿到当前BD对象,bd.getBeanClass().newInstance()得到实例对象(反射)
- 属性注入DI(@Autowired):对象属性上有@Autowired注解的,会递归调用创建过程,这也是为什么会产生循环依赖的原因
- 初始化:回调初始化方法,自定义就实现InitializingBean接口,实现afterPropertiesSet()方法
- 最后将创建完成的Bean放入一级缓存(一个Map)singletonObjects中,并返回
- 在实例化结束后,就将Bean对象直接加入到一级缓存singletonObjects中,但是这个对象是不完整的,多个线程访问可能会拿到不完整的Bean
- 为此,我们可以给一级缓存上锁,当多个线程来访问的时候,会等待当前线程生成完整的Bean对象,再访问,但是有些Bean本身就是完整的,线程也拿不到,效率很明显就下降了
- 为此,我们引入二级缓存earlySingletonObjects,把不完整的对象放入二级缓存,并给二级缓存上锁,完整的对象都放在一级缓存,所以效率大大提升;(二级缓存是为了解决循环依赖,并且能够解决并发下获取完整bena的性能问题)
- 但是如果两个线程,当前线程执行到一级缓存获取,发现没有,另一个线程生成了完整的bean并且将该bean放入一级缓存中,这时候当前线程执行到了二级缓存获取,发现没有,即认为两级缓存都没有,会选择创建,这很明显不应该,所以我们需要双重检查锁,即再检查一次一级缓存是否有该bean
- 正常来说,创建一个对象之后进行AOP动态代理(JDK、CGLIB实现),Spring选择在属性赋值后,初始化时进行AOP动态代理,为此又产生了新的问题:A依赖B,B依赖A的时候,B依赖的A的对象是不完整的,不是动态代理对象。
- 因此,普通的Bean必须在初始化的时候进行动态代理,而产生循环依赖的Bean,必须在属性赋值的时候生成动态代理对象放进二级缓存,并在最后从二级缓存拿出这个动态代理对象放进一级缓存
- Spring要求严格遵守Bean的生命周期,如果所有Bean都在属性赋值后生成动态代理对象,则违反了生命周期。因此我们需要判断是否产生循环依赖,即A对象需要产生第二次,也就是二级缓存里有A对象,我们就产生动态代理对象并将其放入二级缓存中
- 但是又产生新的问题,当A被B和C分别循环依赖,则产生两个动态代理对象;为了保证只创建一次动态代理对象,我们引入三级缓存singletonFactories,即第一次产生动态代理对象会放入三级缓存中,下次先检查三级缓存是否有动态代理对象,有则返回
一级缓存:单例池,里面有所有完整的Bean
二级缓存:解决循环依赖的单例性以及性能问题
三级缓存:为了保证动态代理只创建一次,解决循环依赖,是一个函数接口 解耦,职责单一
推荐阅读
- 为什么Spring中每个Bean都要定义作用域()
- 基础|MyBatis框架知识点总结
- 中间件|(ElasticSearch02)day80分布式查漏补缺
- 构建一个简单的Spring Boot项目
- Java|springboot疫情防控核酸检测管理系统
- spring|基于springboot疫情防控管理系统源码和论文
- spring|springboot实现疫情防控核酸检测管理系统【源码和论文】
- spring|基于springboot社区疫情防控管理系统
- 《带你学》云原生|2022年找工作你必须要会的技能---看了就找工作领先别人一步