Spring的循环依赖问题分析

本文详细介绍了Spring中循环依赖的问题,包括其定义、解决策略以及三级缓存机制。Spring通过setter注入和singleton作用域避免循环依赖问题。在解决循环依赖的过程中,涉及到了singletonObjects、earlySingletonObjects和singletonFactories这三级缓存。文章还概述了创建bean时的流程,并建议读者通过Debug深入理解这一过程。
摘要由CSDN通过智能技术生成

什么是循环依赖?

多个bean之间相互依赖,形成了一个闭环
我们都知道,spring中通过ioc容器,通过反射机制,控制反转,帮我们创建了对象;那么,现在我们要创建一个bean A,但是bean A 依赖的bean B ,而bean B 中又去依赖了bean A,例如下面的构造

class A {
	//依赖b
    private B b;
	
    public A (B b) {
        this.b = b;
    }
}

class B {
	//依赖a
    private A a;

    public B (A a) {
        this.a = a;
    }
}

那么a的创建,需要等待b的创建,b的创建 又需要等待a的创建 (禁止套娃!),因此就出现所谓了循环依赖问题

Spring 中提供的解决策略

循环依赖的报错是 :BeanCurrentlylnCreationException
在这里插入图片描述

spring中提出 我们AB循环依赖问题只要A的注入方式是setter且singleton, 就不会有循环依赖问题 ;默认单例,修改为原型scope=“prototype” 就导致了循环依赖错误
在这里插入图片描述
本质上是依赖三级缓存机制,解决循环依赖问题

Spring 的三级缓存

循环依赖问题的解决,依赖于三级缓存
查看下源码 DefaultSingletonBeanRegistry

在这里插入图片描述

一级缓存 singletonObjects

采用的数据结构是 ConcurrentHashMap;

	private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

第一级缓存〈也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象(实例化完成- > 初始化完成 )

二级缓存 earlySingletonObjects

采用的数据结构是 ConcurrentHashMap

	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);

第二级缓存: earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整)
(实例化完成- > 初始化未完成)

三级缓存 singletonFactories

采用的数据结构是 HashMap

    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

第三级缓存: Map<String, ObiectFactory<?>> singletonFactories,存放可以生成Bean的工厂

解决循环依赖的底层原理

拿上面的AB bean说明 ,创建 A 实例
主要留意下面的四个方法
在这里插入图片描述
下面先来一个大致流程说明

  1. 首先,我们要创建对象 A的 ,获取到 A 的实例
    通过 getSingleton() 去各级缓存中查找, 对象还没创建,缓存中肯定没有
  2. A实例未被创建 ,调用doCreateBean 创建 A的实例
    先实例化A对象 (分配内存空间),将这个 还没初始化的对象,和他的初始化lamda表达式,缓存在第三级缓存singletonFactories中
  3. 开始A对象的属性B注入 调用 populateBean() , 通过 getSingleton() 从各级缓存中查找 B的实例对象 ,没有,那么开始去创建 B的实例对象
  4. 调用doCreateBean 创建 B实例 ,B依赖A ,调用 populateBean() ,又通过 getSingleton() 去各级缓存中查找 A的对象实例 ,这一次,在singletonFactories找到了A的实例缓存,成功实例化了 B ,调用addSingleton()将B缓存到第一级缓存 singletonObjects ,并且将 A 从第三级缓存singletonFactories 移动到 第二级缓存earlySingletonObjects 中 ,执行其中的lamda表达式,继续进行A的实例化
  5. 接下来,A继续去完成自己的属性注入 populateBean() ,getSingleton() 从第一级缓存中找到了 B 的实例对象 ,初始化成功,调用addSingleton()从第二级的缓存移动到第一级别的缓存
  6. 将A实例对象返回

下面还是画图,梳理明白
脑图的地址 https://www.processon.com/view/link/5ffea7f507912914e7e82a57在这里插入图片描述

大家要是先进一步的学明白,还是要通过自己debug打断点,一步一步吃透

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值