Spring是如何解决循环依赖的?

一、什么是循环依赖?

循环依赖:说白了就是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了一个环形调用。

  • 第一种情况:自己依赖自己的直接依赖
    在这里插入图片描述

  • 第二种情况:两个对象之间的直接依赖
    在这里插入图片描述

  • 第三种情况:多个对象之间的间接依赖
    在这里插入图片描述

二、循环依赖的N种场景

在这里插入图片描述

2.1 单例的setter注入

这种注入方式应该是spring用的最多的,代码如下:

@Service
publicclass TestService1 {

    @Autowired
    private TestService2 testService2;

    public void test1() {
    }
}
@Service
publicclass TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}

这是一个经典的循环依赖,但是它能正常运行,得益于spring的内部机制,让我们根本无法感知它有问题,因为spring默默帮我们解决了。

spring内部有三级缓存

  • singletonObjects:一级缓存,用于保存实例化、注入、初始化完成bean实例;
  • earlySingletonObjects:二级缓存,用于保存实例化完成bean实例;
  • singletonFactories: 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。
2.2 多例的setter注入

这种注入方式偶然会有,特别是在多线程的场景下,具体代码如下:

@Service
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class TestService1 {

    @Autowired
    private TestService2 testService2;

    public void test1() {

    }
}
@Service
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {

    }

}

很多人说这种情况spring容器启动会报错,其实是不对的。

其实在 AbstractApplicationContext 类的 refresh方法中告诉了我们答案,它会调用 finishBeanFactoryInitialization方法,该方法的作用是为了spring容器启动的时候提前初始化一些bean。该方法的内部又调用了 preInstantiateSingletons方法
在这里插入图片描述
标红的地方明显能够看出:非抽象、单例并且非懒加载的类才能被提前初始化bean。而多例即SCOPE_PROTOTYPE
类型的类,非单例,不会被提前初始化bean,所以程序能够正常启动。
在这里插入图片描述
只需要再定义一个单例的类,在它里面注入TestService1

@Service
public class TestService3 {

    @Autowired
    private TestService1 TestService1;
}

在这里插入图片描述

注意:这种循环依赖问题是无法解决的,因为它没有用缓存,每次都会生成一个新对象

2.3 构造器注入
@Service
public class TestService4 {

    public TestService4(TestService5 testService5) {
    }
}
@Service
public class TestService5 {

    public TestService5(TestService4 testService4) {
    }
}

运行结果:
在这里插入图片描述

构造器注入只是添加了三级缓存,并没有使用缓存,所以也无法解决循环依赖问题

2.4 单例的代理对象setter注入

这种注入方式其实也比较常用,比如平时使用:@Async 注解的场景,会通过AOP自动生成代理对象

2.5 DependsOn循环依赖

还有一种有些特殊的场景,比如我们需要在实例化Bean A 之前,先实例化Bean B,这个时候就可以使用@DependsOn注解

@Service
@DependsOn(value = "testService9")
public class TestService8 {

    @Autowired
    private TestService9 testService9;

    public void test8() {

    }
}
@Service
@DependsOn(value = "testService9")
public class TestService8 {

    @Autowired
    private TestService9 testService9;

    public void test8() {

    }
}

三、出现循环依赖如何解决?

循环依赖划分

  • 生成代理对象产生的循环依赖
  • 使用@DependsOn产生的循环依赖
  • 其他循环依赖
    • 多例循环依赖
    • 构造器循环依赖
3.1 生成代理对象产生的循环依赖

这类循环依赖问题解决方法很多,主要有:

  1. 使用@Lazy注解,延迟加载;
  2. 使用@DependsOn注解,指定加载先后关系;
  3. 修改文件名称,改变循环依赖类的加载顺序
3.2 使用@DependsOn产生的循环依赖
3.3 多例循环依赖

这类循环依赖问题可以通过把bean改成单例的解决

3.4 构造器循环依赖

这类循环依赖问题可以通过使用@lazy注解解决

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值