Spring是怎么解决的循环依赖

作者: 小石读史 | 来源:发表于2019-12-07 15:34 被阅读0次

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

image

循环依赖会产生什么问题?

循环依赖会造成内存的溢出

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。

相关文章

网友评论

    本文标题:Spring是怎么解决的循环依赖

    本文链接:https://www.haomeiwen.com/subject/snbowctx.html