Spring如何使用三级缓存解决循环依赖

首先来了解一下什么是循环依赖

@Component
public class A { @Autowired
B b;
} @Component
public class B { @Autowired
A a;
}

在对象A创建过程中,需要注入B,因为容器中没有B,则去创建B,B创建过程中又需要注入A,而A在等待B的创建,B在等待A的创建,导致两者都无法创建成功,无法加入到单例池供用户使用。

Spring则通过三级缓存来解决循环依赖的问题,另外如果对象的作用范围是Prototype,则无法通过三级缓存解决循环依赖,会抛出BeanCurrentlyInCreationException异常,构造注入的方式,也无法解决循环依赖,只有set注入可以解决。

那么三级缓存又是什么呢?

三级缓存就是三个Map

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	//一级缓存(单例池,经过完成生命周期的对象会放入其中)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //二级缓存(刚实例化还未初始化的原始对象会放入其中)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); //三级缓存(存放创建某个对象的工厂)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

Spring Bean对象从创建到初始化大致会经过四个流程

getSingleton()doCreateBean()populateBean()addSingleton()

  • getSingleton:从单例池中获取bean对象,如果没有,则进行创建

  • doCreateBean():创建bean对象

  • populateBean():填充依赖,如果被填充的对象不存在于单例池,则进行创建等四个流程

  • addSingleton():将初始化完成的对象加入到单例池

循环依赖的对象在三级缓存中的迁移过程

  • A 创建过程中需要 B, 于是 A 将自己放到三级缓存里面,去实例化 B

  • B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存

    找到了A,然后把三级缓存中的 A 放到二级缓存,并删除三级缓存中的 A

  • B 顺利初始化完毕,将自己放到一级缓存中(此时 B 中的 A 还是创建中状态,并没有完全初始化),删除三级缓存中的 B

    然后接着回来创建 A,此时 B 已经完成创建,直接从一级缓存中拿到 B,完成 A 的创建,并将 A 添加到单例池,删除二级缓存中的 A

图示:

Spring如何使用三级缓存解决循环依赖的更多相关文章

  1. Spring三级缓存解决循环依赖

    前提知识 1.解决循环依赖的核心依据:实例化和初始化步骤是分开执行的 2.实现方式:三级缓存 3.lambda表达式的延迟执行特性 spring源码执行逻辑 核心方法refresh(), popula ...

  2. Spring ioc(4)---如何解决循环依赖

    前面说到对象的创建,那么在创建的过程中Spring是怎么又是如何解决循环依赖的呢.前面提到有个三级缓存.就是利用这个来解决循环依赖.打个比方说实例化A的时候,先将A创建(早期对象)放入一个池子中.这个 ...

  3. spring: 我是如何解决循环依赖的?

    1.由同事抛的一个问题开始 最近项目组的一个同事遇到了一个问题,问我的意见,一下子引起的我的兴趣,因为这个问题我也是第一次遇到.平时自认为对spring循环依赖问题还是比较了解的,直到遇到这个和后面的 ...

  4. 曹工说Spring Boot源码(29)-- Spring 解决循环依赖为什么使用三级缓存,而不是二级缓存

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  5. Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?

    前言 在研究 『 Spring 是如何解决循环依赖的 』 的时候,了解到 Spring 是借助三级缓存来解决循环依赖的. 同样在上一节留下了疑问: 循环依赖为什么要使用三级缓存?而不是使用二级缓存? ...

  6. Spring如何解决循环依赖问题

    目录 1. 什么是循环依赖? 2. 怎么检测是否存在循环依赖 3. Spring怎么解决循环依赖 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可 ...

  7. Spring解决循环依赖,你真的懂了吗?

    导读 前几天发表的文章SpringBoot多数据源动态切换和SpringBoot整合多数据源的巨坑中,提到了一个坑就是动态数据源添加@Primary接口就会造成循环依赖异常,如下图: 这个就是典型的构 ...

  8. Spring如何解决循环依赖,你真的懂了?

    导读 前几天发表的文章SpringBoot多数据源动态切换和SpringBoot整合多数据源的巨坑中,提到了一个坑就是动态数据源添加@Primary接口就会造成循环依赖异常,如下图: 这个就是典型的构 ...

  9. Spring是如何解决循环依赖的

    前言 在面试的时候这两年有一个非常高频的关于spring的问题,那就是spring是如何解决循环依赖的.这个问题听着就是轻描淡写的一句话,其实考察的内容还是非常多的,主要还是考察的应聘者有没有研究过s ...

随机推荐

  1. Access数据库及注入方法

    目录 Access数据库 Access数据库中的函数 盲注Access数据库 Sqlmap注入Access数据库 Access数据库 Microsoft Office Access是由微软发布的关系数 ...

  2. CVE-2013-3346:十全九美的 Adobe Reader ToolButton UAF 漏洞

    0x01 "Epic Turla" 网络间谍行动 在 2014 年 8 月,被誉为 "世界十大最危险的网络攻击行动" 之一的 "Epic Turla& ...

  3. Docker用Dockerfile定制镜像

    用Dockerfile定制镜像 镜像的定制实际上就是定制每一层所添加的配置.文件.如果我们可以把每一层修改.安装.构建.操作的命令都写入一个脚本,用这个脚本来构建.定制镜像,那之前提示的无法重复的问题 ...

  4. Swift系列一 - 数据类型

    如果你习惯了OC的语法,第一次接触Swift的语法可能会有点抗拒,因为Swift的语法有点怪.但如果你有前端的基础,学Swift可能会有点吃力,如果你有C++的基础可能会学得比较快点.不管你有什么样的 ...

  5. JVM默认内存大小

    堆(Heap)和非堆(Non-heap)内存 按照官方的说法:"Java虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在Java虚拟机启动时创建的." ...

  6. Elastic-Job原理

    概述Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成. Elastic-Job-Lite定位为轻量级无中心 ...

  7. webpack 快速入门 系列 —— 初步认识 webpack

    初步认识 webpack webpack 是一种构建工具 webpack 是构建工具中的一种. 所谓构建,就是将资源转成浏览器可以识别的.比如我们用 less.es6 写代码,浏览器不能识别 less ...

  8. Webpack5构建速度提升令人惊叹,早升级早受益

    为什么要升级? webpack4用的好好的,运行稳定,为什么要升级到webpack5, 每次升级,都要经历一场地震,处理许多loader和plugin API的破坏性改变. 请给我们一个充分的升级理由 ...

  9. 老J的技术分享之总结

    老J做IT这块有二十多个年头了,算是中国IT的见证者与参与者.那个时候刚开始接触和了解时,对于他的一些建议,我不是很乐于去接受,因为我觉得他的那一套技术体系不是很适合如今的情况,当时间久了后发现,他对 ...

  10. xxl-job源码阅读一(客户端)

    1.源码入口 使用xxl-job的时候,需要引入一个jar,然后还需要往Spring容器注入XxlJobSpringExecutor <dependency> <groupId> ...