首页 / 知识
Spring中的循环依赖
2023-04-11 16:20:00

一、什么是循环依赖?
例如,就是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的生命周期。
二、Bean的生命周期
2.1 Spring Bean 的生命周期
具体看这篇博客–》 Spring Bean 的生命周期
2.2 Bean 的生成步骤
被 Spring 管理的对象叫做 Bean 。Bean的生成步骤如下:
Spring 扫描 class 得到 BeanDefinition;
根据得到的 BeanDefinition 去生成 bean;
首先根据 class 推断构造方法;
根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象);
填充原始对象中的属性(依赖注入);
如果原始对象中的某个方法被 AOP 了,那么则需要根据原始对象生成一个代理对象;
把最终生成的代理对象放入单例池(源码中叫做 singletonObjects)中,下次 getBean 时就直接从单例池拿即可;
对于 Spring 中的 Bean 的生成过程,步骤还是很多的,并且不仅仅只有上面的7步,还有很多很多,这里不详细说了。
我们可以发现,在得到一个原始对象后,Spring 需要给对象中的属性进行依赖注入,那么这个注入过程是怎样的?
比如上文说的 A 类,A 类中存在一个 B 类的 b 属性,所以,当 A 类生成了一个原始对象之后,就会去给 b 属性去赋值,此时就会根据 b 属性的类型和属性名去 BeanFactory 中去获取 B 类所对应的单例bean。
1. 如果此时 BeanFactory 中存在 B 对应的 Bean,那么直接拿来赋值给 b 属性;
2. 如果此时 BeanFactory 中不存在 B 对应的 Bean,则需要生成一个 B 对应的 Bean,然后赋值给 b属性。
问题就出现在「第二种」情况,如果此时 B 类在 BeanFactory 中还没有生成对应的 Bean,那么就需要去生成,就会经过 B 的 Bean 的生命周期。
那么在创建 B 类的 Bean 的过程中,如果 B 类中存在一个 A 类的 a 属性,那么在创建 B 的 Bean 的过程中就需要 A 类对应的 Bean,但是,触发 B 类 Bean 的创建的条件是 A 类 Bean 在创建过程中的依赖注入,所以这里就出现了循环依赖:
A Bean创建–>依赖了 B 属性–>触发 B Bean创建—>B 依赖了 A 属性—>需要 A Bean(但A Bean还在创建过程中)
从而导致 A Bean 创建不出来,B Bean 也创建不出来。
这是循环依赖的场景,但是上文说了,在 Spring 中,通过某些机制帮开发者解决了部分循环依赖的问题,这个机制就是「三级缓存」。
三、三级缓存
一级缓存为:singletonObjects;
二级缓存为:earlySingletonObjects;
三级缓存为:singletonFactories;
/** Cache of singleton objects: bean name –> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);
/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
3.1三个缓存分别有什么作用
「singletonObjects」中缓存的是已经经历了完整生命周期的bean对象。
「earlySingletonObjects」比 singletonObjects 多了一个 early ,表示缓存的是早期的 bean对象。早期指的是 Bean 的生命周期还没走完就把这个 Bean 放入了 earlySingletonObjects。
「singletonFactories」中缓存的是 ObjectFactory,表示对象工厂,用来创建某个对象的。
|
最新内容
相关内容
文本处理用c还是用python
文本处理用c还是用python,位置,培训,包装,对比,字符串,函数,文本,语言,字符,效率,文本处理python与c的对比:如下c++语言:C++语言实现C++中没有python如何调用另一个文件夹中的内
python如何调用另一个文件夹中的内容?,系统,培训,文件,模块,内容,路径,函数,所在,前缀,语句,python中调用另外一个文件夹中的内容:1、同一文件python中怎么对一个数进行因式分解
python中怎么对一个数进行因式分解?,代码,培训,因式分解,因数,个数,最小,整数,数组,假定,分解,1、Python因式分解代码:importtime#对一个数进机器学习用java还是python?
机器学习用java还是python?,分析,环境,数据,培训,发展,机器,结果,控制台,生态环境,有用,机器学习用python更合适。机器学习不需要面向对象,不python是解释型吗?
python是解释型吗?,工作,平台,培训,解释性,虚拟机,语言,高层次,源码,脚本语言,之前,python是一种跨平台的计算机程序设计语言。是一个高层次Python怎么取出列表中的相邻元素?
Python怎么取出列表中的相邻元素?,代码,异常,培训,元素,指针,序列,对象,表示,语句,函数,1、python的迭代器。iter()能把一个序列生成为一个和python怎么在数组添加一行?
python怎么在数组添加一行?,培训,下标,维度,数组,列表,函数,形状,元素,代表,原型,python中在数组添加一行的方法:python中可以使用stack()函数python怎么判断文档是否有指定后缀
python怎么判断文档是否有指定后缀?,培训,后缀,文档,文件夹,路径,以上,文件,方法,更多,内容,python判断文档是否有指定后缀的方法:importosYoupython什么时候加self?
python什么时候加self?,培训,变量,方法,作用,参数,示例,函数,实例,前面,下面,想要知道python什么时候加self就需要知道self的作用。下面我们Python如何复制文件中的内容
Python如何复制文件中的内容,盘中,数据,培训,文件,内容,方法,文件夹,路径,源文件,文件名,python复制文件中内容的方法:1、使用shutil.copyfilepython函数里面形参和实参一样吗?
python函数里面形参和实参一样吗?,培训,函数,参数,里面,变量,实际,形式,全称,示例,后面,python函数里面形参和实参不一样。形参全称是形式参实数是不是python的数据类型?
实数是不是python的数据类型?,数字,标准,培训,实数,数据类型,数轴,复数,有限小数,无理数,虚数,实数是python的数据类型。实数,是有理数和无理