首先我们要知道什么是循环依赖,循环依赖就是如下图所示,两个或多个bean之间相互引用,最终形成一个封闭的环。

循环依赖会产生什么问题?
循环依赖会造成内存的溢出
spring中的循环依赖有三种情况:
1、构造器参数循环依赖
构造器的循环依赖是不可以解决的,Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时会抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。
案例:
public class StudentA {
private StudentB studentB ;
public void setStudentB(StudentB studentB) {
this.studentB = studentB;
}
public StudentA() {
}
public StudentA(StudentB studentB) {
this.studentB = studentB;
}
}
public class StudentB {
private StudentC studentC ;
public void setStudentC(StudentC studentC) {
this.studentC = studentC;
}
public StudentB() {
}
public StudentB(StudentC studentC) {
this.studentC = studentC;
}
}
public class StudentC {
private StudentA studentA ;
public void setStudentA(StudentA studentA) {
this.studentA = studentA;
}
public StudentC() {
}
public StudentC(StudentA studentA) {
this.studentA = studentA;
}
}
在上面三个对象实例化的时候,Spring容器先创建单例StudentA,StudentA有参构造依赖StudentB,此时将A放在“当前创建Bean池”中,此时创建StudentB,StudentB的有参构造依赖StudentC ,然后将B放在“当前创建Bean池”中,此时创建StudentC,StudentC的有参构造又依赖StudentA, 但是,此时StudentA已经在池中,所以会报错,因为在池中的Bean都是未初始化完的,所以会依赖错误。
2、setter循环依赖
Spring bean的创建实际上还是一个对象的创建,一个对象创建包含两部分:当前对象实例化和对象属性的实例化。在Spring中,对象的实例化是通过反射实现的,而对象的属性则是在对象实例化之后通过一定的方式设置的。
Spring在实例化一个bean的时候,首先递归实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。
假设场景如下,A->B->A
(1)实例化A,并将未注入属性的A暴露出去,即提前曝光给容器Wrap
(2)开始为A注入属性,发现需要B,调用getBean(B)
(3)实例化B,并注入属性,发现需要A的时候,从单例缓存中查找,没找到时继而从Wrap中查找,从而完成属性的注入
(4)递归完毕之后回到A的实例化过程,A将B注入成功,并注入A的其他属性值,自此即完成了循环依赖的注入
3、prototype范围的依赖
对于"prototype"作用域bean,Spring容器无法完成依赖注入,因为Spring容器不缓存"prototype"作用域的bean,因此无法提前暴露一个创建中的bean。
网友评论