你知道Spring是如何解决循环依赖的吗?

22 篇文章 0 订阅
7 篇文章 0 订阅

前沿

我认为Spring 的2大基石是IOC和AOP,IOC称为控制反转,能够将bean之前的依赖关系交给Spring来维护,开发者无需关注这些,安心写自己的业务逻辑代码即可。但是由于依赖关系随着时间的积累,变得越来越复杂,在某一个时间点,系统启动的时候,告诉你

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

发生了Spring无法解决的循环依赖问题。

这篇文章我们的讨论分为3个方面:循环依赖的情景、Spring能够解决哪些循环依赖、Spring是如何解决循环依赖。

正文

我们新创建2个类User和School

循环依赖场景及Spring能够解决哪些循环依赖

序列依赖方式Spring是否能解决
1都是通过set方式相互依赖可以
2都是通过构造器相互依赖不能
3User通过构造器与School依赖,School通过set方式与User依赖可以
4School通过构造器与User依赖,User通过set方式与User依赖可以

下面是例子

第一种情况:

@Component
public class User {
    private String name;
    private School school;
    /*User(School school){
        this.school = school;
    }*/
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }
}

@Component
public class School {
    private User user;
    private String name;

    /*School(User user) {
        this.user = user;
    }*/

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

项目启动后,正常

第二种情况:

@Component
public class User {
    private String name;
    private School school;
    User(School school){
        this.school = school;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}



@Component
public class School {
    private User user;
    private String name;

    School(User user) {
        this.user = user;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

启动后报错,报错信息如下:

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐

|  school defined in file [/Users/zanpengfei/workspace/user/target/classes/com/zanpengfei/user/user/circle/School.class]

↑     ↓

|  user defined in file [/Users/zanpengfei/workspace/user/target/classes/com/zanpengfei/user/user/circle/User.class]

└─────┘

Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

School通过构造器与User依赖,User通过set方式与School依赖

@Component
public class User {
    private String name;
    private School school;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }
}


@Component
public class School {
    private User user;
    private String name;

    School(User user) {
        this.user = user;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

启动后正常

User通过构造器与School依赖,School通过set方式与User依赖

@Component
public class School {
    private User user;
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

@Component
public class User {
    private String name;
    private School school;
    User(School school){
        this.school = school;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

启动后正常

Spring如何解决循环依赖

Spring通过三级缓存来解决循环依赖,其中一级缓存为单例池(singletonobjects集合),二级缓存为早期曝光对象集合earlysingletonobjects,三级缓存为早期曝光对象工厂(

ingletonFactories集合)

1、当User、School两个类发生循环引用时,在User类完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中。如果User被AOP代理,那么通过这个工厂获取到的就是User代理后的对象,如果User没有被AOP代理,那么这个工厂获取到的就是User实例化的对象。

2、当User进行属性注入时,会去创建School、同时 School又依赖了User、所以创建School的同时又会去调用getBean(user)来获取需要的依赖。此时的getBean(user)会从缓存中获取:

首先先获取到三级缓存中的工厂

而后再调用对象工厂的getobject方法来获取到对应的对象,得到这个对象后将其注入到School中。紧接着School会走完它的生命周期流程(初始化、后置处理器等)。

当School创建完后,会将School再注入到User中,此时User再完成它的整个生命周期。到此循环依赖结束!

Spring为啥不能解决都是通过构造器相互依赖呢?

第一步,User类完成实力化的时候,也就是调用自己的构造器,发送构造器中存在Scholl类,然后开始实例化School类,当调用School构造器的时候发现依赖与User类,再实例化User类,发现User类依赖于School类等等,发生了循环依赖,所以这种情况下不能解决

Spring为啥能解决都是通过set方式相互依赖呢?

第一步,User类进行实例化,调用构造方法,因为无有参构造器,调用其无参构造器,完成实例化,实例化后通过User创建一个工厂,并且添加到三级缓冲中。

第二步,User类进行属性注入,在属性注入的时,会创建School类,School类进行实例化,同样School类无有参构造方法,调用无参构造器进行实例化,实例化后创建一个School工厂,并将其添加到三级缓冲,然后进行属性注入,属性注入时,发现依赖与User,通过调用getBean(user)获取依赖性对象,虽然User类还不完整,但是其已经完成实例化并且将其工厂类添加到了三级缓冲中,getBean(user)会调用User的工厂类返回user对象,将User注入到School上,然后School完成整个生命周期,最后返回School对象给User类,School类注入到了User上,User类继续完成整个生命周期,至此循环依赖结束。

剩下2种情况也是类似的,看完后大家了解Spring解决循环依赖的原理了吗

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值