Spring IOC 如何解决循环依赖?

前言

假设对象A、B 之间相互依赖,Spring IOC是如何解决A、B两个对象的实例化的?答案是三级缓存

三级缓存

SpringIOC 通过三级缓存来解决循环依赖问题,三级缓存指的是三个Map:

  • singletonObjects:一级缓存,key为BeanName,value为Bean,日常获取Bean的地方
  • earlySingletonObjects:二级缓存,key为BeanName,value为Bean,已经实例化但还没有进行属性注入的Bean,由三级缓存放入
  • singletonFactories:三级缓存,key为BeanName,value为对象工厂(ObjectFactory)

在实际使用中,要获取一个bean,先从一级缓存一直查找到三级缓存,缓存bean的时候是从三级到一级的顺序保存,并且缓存bean的过程中,三个缓存都是互斥的,只会保持bean在一个缓存中,而且,最终都会在一级缓存中。

解决循环依赖

SpringIOC解决循环依赖的思路就是依靠缓存,同时还得引出个概念即早期暴露引用。我们知道在IOC容器里Bean的初始化的过程分为三个步骤:创建实例、属性注入实例、回调实例实现的接口方法

解决思路就在这:当我们创建实例与属性注入实例这俩个步骤之间的时候,我们引入缓存,将这些已经创建好但是并没有注入属性的实例放到缓存里,而这些放在缓存里但是没有被注入属性的实例对象,就是解决循环依赖的方法

打个比方:A对象的创建需要引用到B对象,而B对象的创建也需要A对象,而此时当B对象创建的时候直接从缓存里引用A对象(虽然不是完全体A对象,毕竟没有赋值处理),当B对象完成创建以后再被A对象引用进去,则A对象也完成了创建。

解决循环依赖具体过程

对Bean的创建最为核心三个方法解释如下:

  • createBeanInstance:实例化,通过反射调用对象构造方法实例化对象;
  • populateBean:填充属性,主要是对bean的依赖属性进行赋值;
  • initializeBean:初始化,可以回调InitializingBean、initMethod等方法。

实例化A的时候,先将A创建(早期对象)放入一个池子(singletonFactories)中。这个时候虽然属性没有赋值,但是容器已经能认识这个是A对象,只是属性全是null而已。在populateBean方法中对属性赋值的时候,发现A依赖了B,那么就先去创建B,又走一遍bean的创建过程(创建B)。同样也会把B的早期对象放入缓存(singletonFactories)中。当B又走到 populateBean方法(负责填充Bean实例属性的方法)的时候,发现依赖了A,我们又去创建A,但是这个时候去创建A,发现我们在缓存(singletonFactories)能找到A(早期对象),此时会通过A的ObjectFactory获取A,并把A从三级缓存移到二级缓存。然后就可以把B的A属性赋值了,这个时候B就初始化完成了,初始化完成后就会把B从三级缓存移到一级缓存。完成B实例化后,回到A调用的populateBean方法中。返回的就是B对象了,对A的B属性进行赋值就可以了。

流程如下:图片来源
在这里插入图片描述

IOC无法解决的两种循环依赖

一种是非单例对象,因为非单例对象不会放入缓存的。每次都是需要创建。

二是通过构造器注入,也无法解决。从上面的流程可以看出,调用构造器创建实例是在createBeanInstance方法,而解决循环依赖是在populateBean(负责属性注入的方法)这个方法中,执行顺序也决定了无法解决该种循环依赖。

为什么采用三级缓存?

一级缓存是单例缓存池(singletonObjects)

二级缓存是早期对象(earlySingletonObjects)

三级缓存是一个包裹对象ObjectFactory(registeredSingletons),通过getObject获取到早期对象。

从上面的流程来看,实际上二级缓存已经可以解决循环依赖了,那么为什么Spring还要包裹出来一个三级缓存呢?

三级缓存其实是为了解决代理对象之间(AOP)的循环依赖。如果没有三级缓存,在对象被AOP代理的情况下,存入二级缓存前都需要先去做AOP代理。二级缓存存在的必要就是为了性能,从三级缓存的工厂创建出对象,直接放入二级缓存,避免每次都从工厂中获取。.

通过第三级缓存我们可以拿到可能经过包装的对象,解决对象代理封装的问题。

三级缓存的value是ObjectFactory,ObjectFactory 的 getObject 如果包装的对象被AOP代理,则会返回相应的代理对象

具体源码分析可以参考:Spring ioc(4)—如何解决循环依赖

三级缓存的划分及作用

一级缓存 singletonObjects完整的bean,它可以被外界任意使用,并且不会有歧义。

二级缓存 earlySingletonObjects不完整的bean,没有完成初始化,它与singletonObjects的分离主要是职责的分离以及边界划分,可以试想一个Map缓存里既有完整可使用的bean,也有不完整的,只能持有引用的bean,在复杂度很高的架构中,很容易出现歧义,并带来一些不可预知的错误。

三级缓存 singletonFactories其职责就是包装一个bean,有回调逻辑,主要用于解决代理对象的循环依赖,所以它的作用非常清晰,并且只能处于第三层。

在实际使用中,要获取一个bean,先从一级缓存一直查找到三级缓存,缓存bean的时候是从三级到一级的顺序保存,并且缓存bean的过程中,三个缓存都是互斥的,只会保持bean在一个缓存中,而且,最终都会在一级缓存中

总结

  1. SpringIOC 通过三级缓存解决循环依赖
  2. 要获取一个bean,先从一级缓存一直查找到三级缓存,缓存bean的时候是从三级到一级的顺序保存,并且缓存bean的过程中,三个缓存都是互斥的,只会保持bean在一个缓存中,而且,最终都会在一级缓存中。
  3. Bean在实例化后(createBeanInstance)、属性注入前(populateBean),会先将属性为null的Bean包装成对象工厂(ObjectFactory)放入三级缓存中,在属性注入过程中会依次从一级到三级查询缓存查找依赖的Bean,不存在则先实例化依赖的Bean,完成属性注入。Bean初始化完成后,会被放入一级缓存
    4.三级缓存其实是为了解决代理对象之间(AOP)的循环依赖,通过第三级缓存我们可以拿到可能经过包装的对象,解决对象代理封装的问题。

参考

  • 实例创建流程_从Spring Bean创建流程中看三级缓存解决循环依赖
  • 好文:Spring Bean 循环依赖为什么需要三级缓存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330060.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pythondocx模板_使用python-docx-template修改word文档

由于最近工作中需要自动修改word文档,并生成PDF文件,经过查阅资料后发现使用python-docx-template可以完成对word的修改工作,于是记录一下使用方法。文章内容大部分来自对以下博客的整理和学习https://blog.csdn.net/weixin_42670653/article…

面试必问的 CAS ,要多了解

转载自 面试必问的 CAS ,要多了解前言 CAS(Compare and Swap),即比较并替换,实现并发算法时常用到的一种技术,Doug lea大神在java同步器中大量使用了CAS技术,鬼斧神工的实现了多线程执行的安全性…

MySQL 对于千万级的大表要怎么优化?

很多人第一反应是各种切分; 我给的顺序是: 第一 优化你的sql和索引; 第二 加缓存,memcached,redis; 第三 以上都做了后,还是慢,就做主从复制或主主复制,读写分离,可以在应用层做&…

MySQL元数据库——information_schema

转自: https://www.cnblogs.com/postnull/p/6697077.html 平时使用MySQL客户端操作数据库的同学,只要稍微留神都会发现,除了我们建的库之外,还经常看到三个数据库的影子: 1. information_schema 2. performance_sche…

mysql 表字段信息从一张表迁移到另一张表_MySQL(数据库)笔记

###数据库之前通过流去操作文件保存数据库的弊端:1.执行效率低2.开发成本高3.一般只能保存小量数据4.只能保存文本数据####什么是DB- DataBase 数据库:代表文件集合####什么是DBMS- DataBaseManagementSystem 数据库管理系统(软件),用于管理保存数据的文件集合,用于和程序员进行…

GET与POST传递数据的最大长度能够达到多少

各种web开发语言中,各个页面之间基本都会进行数据的传递,web开发里面比较常用的数据传递方式有get post,一直以来我都只知道get传递的数据量要比post传递的数据量要少,所以传递大数据量还是要用post,但是 get post 这两…

maven命令实战

【1】 创建maven项目 1)目录结构 mavenhello09|---src|---|---main|---|---|---java|---|---|---resources|---|---test|---|---|---java|---|---|---resources|---pom.xml 目录结构说明: main/java:主程序;main/resources&…

Mac 环境变量配置

环境变量配置 cd ~ (回到主目录home)如果你是第一次配置环境变量,可以使用“touch .bash_profile” 创建一个.bash_profile的隐藏配置文件vim .bash_profile,写入相应的环境变量,如下: # golang配置 export GOROOT/usr/local/Ce…

python测试开发面试题_python测试开发面试之深浅拷贝

先来道题热热身 a (a, b,c) c copy.copy(a) d copy.deepcopy(a) if c d: print("c和d的值相等") if id(c) id(d): print("c和d的地址相等") 想想最后打印的是什么? 什么是深拷贝和浅拷贝 深拷贝,就是在对某个对象进行拷贝的时候…

linux虚拟机tomcat上部署web项目的常用命令

1)查看 tomcat是否在运行 ps -ef | grep tomcat ps -ef 补充:Linux中的ps命令是Process Status的缩写,ps命令用来列出系统中当前运行的那些进程。ps命令可以列出当前进程的运行情况(状态、时间等信息)。在Linux系统中…

一文理清Cookie、Session、Token

发展史 1、很久很久以前,Web 基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的HTTP协议, 就是请求加响应, 尤其是我不用记…

python中debug有什么用途_史上最方便的Python Debug工具

最近在github上冒出了一个python的debug神器PySnooper,号称在debug时可以消灭print。那么该工具有哪些优点呢,如何使用该工具呢。本文就介绍该工具的优缺点和使用方式。 前言 使用python开发过程中,总是避免不了debug。传统的debug过程大致分…

能力陷阱总结

【readme】 本文总结于《能力陷阱》,感觉非常不错,有兴趣的朋友可以看下; 【1】领导者的能力陷阱 1)改变思想从行动开始; 改变做事方式,然后才能改变自己的思考方式; 2)要先在行…

Java HashMap 遍历方式性能探讨

转载自 Java HashMap 遍历方式性能探讨关于HashMap的实现这里就不展开了,具体可以参考JDK7与JDK8中HashMap的实现JDK8之前,可以使用keySet或者entrySet来遍历HashMap,JDK8中引入了map.foreach来进行遍历。原因:keySet其实是遍历了…

Raft共识算法

前提条件 Raft不考虑拜庭将军问题,即消息会延迟、丢失但不会错误。 Raft的特性 Strong leader:在 Raft 中,日志条目(log entries)只从 leader 流向其他服务器。 这简化了复制日志的管理,使得 raft 更容易…

直方图 帕累托图_如何发现现象背后的关键因素?帕累托图,质量管理的利器...

大家好:无论在日常生活还是工作中,都会发生一些事情或者结果,是我们不希望发生的。我们也希望从根本上解决掉,但有时候却无从下手,比如本来打算用来看书的时间不知道去哪里了;本来打算存款的钱也不知道去哪…

centos8上安装nginx

参考自 https://www.jianshu.com/p/9b2dd37a5af9 ; 【1】安装步骤 step1)安装nginx sudo yum install -y nginx step2)启动nginx服务 -- 开机自启动 sudo systemctl enable nginx -- 开启nginx 服务 sudo systemctl start nginx step3&am…

简单分析KafKa工作原理

架构图 Producer:Producer即生产者,消息的产生者,是消息的入口。 kafka cluster: Broker:Broker是kafka实例,每个服务器上有一个或多个kafka的实例,我们姑且认为每个broker对应一台服务器。每…

Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

转载自 Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析网上关于 HashMap 和 ConcurrentHashMap 的文章确实不少,不过缺斤少两的文章比较多,所以才想自己也写一篇,把细节说清楚说透,尤其像 Java8 中的 ConcurrentHashMap&#…

kali mysql停止服务器_MySQL 的主从复制(高级篇)

首先要明白为什么要用 mysql 的主从复制:1–在从服务器可以执行查询工作 (即我们常说的读功能),降低主服务器压力;(主库写,从库读,降压)2–在从主服务器进行备份,避免备份期间影响主…