Spring-循环依赖简述

news2024/10/6 18:29:16

什么是循环依赖

// A依赖了B
class A
{
    public B b;
}
​
// B依赖了A
class B
{
    public A a;
}
​
// 循环依赖
A a = new A();
B b = new B();
a.b = b;
b.a = a;

对象之间的相互依赖很正常,但是在Spring中由于对象创建要经过Bean的生命周期,所以就有了循环依赖问题

Bean的生命周期

在Spring中,被Spring管理的对象叫做Bean,那么Bean是如何生成的?

1、Spring扫描class得到BeanDefinition,放在一个Map里

2、根据得到的BeanDefinition去生成Bean

3、首先根据class推断构造方法

4、根据推断出来的构造方法,反射得到一个对象

5、填充原始对象中的属性,依赖注入

6、如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象

7、把最终生成的代理对象放入单例池中,下次getBean直接从单例池拿即可

这里只列举出大致流程,还有Aware回调、初始化等

出现循环依赖的流程简述:

ABean创建-->依赖了B属性-->触发BBean创建--->B依赖了A属性--->需要ABean(但ABean还在 创建过程中)

在Spring中,就是通过三级缓存机制解决的循环依赖问题

三级缓存

一级缓存为:singletonObjects;二级缓存为:earlySingletonObjects;三级缓存为:singletonFactories

三个缓存的作用:

  • singletonObjects中缓存的是已经经历了完整生命周期的bean对象。

  • earlySingletonObjects比singletonObjects多了一个early,表示缓存的是早期的bean对象。也就是Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects。

  • singletonFactories中缓存的是ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂。

循环依赖问题:A创建时--->需要B---->B去创建--->需要A,从而产生了循环

打破循环依赖,可以加个中间人(缓存)

一个缓存就能解决循环依赖了,那么为什么Spring中还需要singletonFactories呢?

如果A的原始对象注入给B的属性之后,A的原始对象进行了AOP产生了一个代理对象,此时对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的a属性对应的并不是AOP之后的代理对象,这就产生了冲突。B依赖的A和最终的A不是同一个对象

现在Spring所用的singletonFactories,为了调和不同的情况,在singletonFactories中存的是lambda表达式,这样的话,只有在出现了循环依赖的情况,才会执行lambda表达式,才会进行AOP,也就说只有在出现了循环依赖的情况下才会打破Bean生命周期的设计,如果一个Bean没有出现循环依赖,那么它还是遵守了Bean的生命周期的设计的。

singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中,生成完原始对象之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口,所以支持Lambda表达式:() -> getEarlyBeanReference(beanName, mbd, bean)

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}
​
// InstantiationAwareBeanPostProcessorAdapter
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    return bean;
}
​
// AbstractAutoProxyCreator(AnnotationAwareAspectJAutoProxyCreator的父类,进行AOP用到)
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}

总结

1、singletonObjects:缓存经过了完整生命周期的bean

2、earlySingletonObjects:缓存未经过完整生命周期的bean,如果某个bean出现了循环依赖,就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,这个bean如果要经过AOP,那么就会把代理对象放入earlySingletonObjects中,否则就是把原始对象放入earlySingletonObjects

3、singletonFactories:缓存的是一个ObjectFactory,也就是一个Lambda表达式。在每个Bean的生成过程中,经过实例化得到一个原始对象后,都会提前基于原始对象暴露一个Lambda表达式,并保存到三级缓存中,这个Lambda表达式可能用到,也可能用不到,如果当前Bean没有出现循环依赖,那么这个Lambda表达式没用,当前bean按照自己的生命周期正常执行,执行完后直接把当前bean放入singletonObjects中,如果当前bean在依赖注入时发现出现了循环依赖,则从三级缓存中拿到Lambda表达式,并执行Lambda表达式得到一个对象,并把得到的对象放入二级缓存。

4、还有一个缓存,就是earlyProxyReferences,用来记录某个原始对象是否进行过AOP了。在AbstractAutoProxyCreator的postProcessAfterInitialization方法中,会去判断当前beanName是否在earlyProxyReferences,如果在则表示已经提前进行过AOP了,无需再次进行AOP。

思考:为什么需要singletonFactories?假设没有singletonFactories,只有earlySingletonObjects会怎样?

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

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

相关文章

【MySQL数据库】 六

本文主要介绍了数据库原理中数据库索引和事务相关概念. 一.索引 在查询表的时候,最基本的方式就是遍历表,一条一条筛选 . 因此,就可以给这个表建立索引,来提高查找的速度 比如,按照id建立索引 在数据库上额外搞一个空间维护一些id 相关的信息, id:1 表的某个位置 id:2 …

第22章_数据库的设计规范

文章目录 范式的概念三范式范式一范式二范式三 反范式总结 范式的概念 为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库&#xff…

LLM 大模型向量数据库技术架构浅析

▼最近直播超级多,预约保你有收获 近期直播:《LLM 大模型向量数据库技术架构剖析和应用案例实战》 —1— AI 智能时代,开发者需要一个真正的向量数据库吗? 答案很简单,这取决于开发者的应用场景。举个例子,…

【C++】多态(重写)的实现过程及其原理【核心知识点精讲】(22)

前言 大家好吖,欢迎来到 YY 滴C系列 ,热烈欢迎! 本章主要内容面向接触过C的老铁 主要内容含: 欢迎订阅 YY滴C专栏!更多干货持续更新!以下是传送门! 目录 一.基础知识介绍1)虚函数&a…

It Is All About Data: A Survey on the Efects of Data on Adversarial Robustness

It Is All About Data: A Survey on the Effects of Data on Adversarial Robustness----《这一切都关乎数据:关于数据对对抗性鲁棒性影响的调查》 摘要 对抗性示例是攻击者故意设计的机器学习模型的输入,目的是迷惑模型,使其犯错误。这些例…

Java对象的深拷贝

什么是深拷贝 在Java中,对象的深拷贝是指创建一个新的对象,并复制原始对象的所有字段和属性,包括嵌套对象。深拷贝确保原始对象和拷贝对象是完全独立的,对其中一个对象的修改不会影响另一个对象。 深拷贝需要注意的点 在Java中…

震裕科技-300953 三季报分析(20231108)

震裕科技-300953 基本情况 公司名称:宁波震裕科技股份有限公司 A股简称:震裕科技 成立日期:1994-10-18 上市日期:2021-03-18 所属行业:专用设备制造业 周期性:0 主营业务:精密级进冲压模具及下游…

【hcie-cloud】【5】华为云Stack规划设计之华为云Stack标准化配置、缩略语【下】

文章目录 前言、华为云Stack交付综述为云Stack标准组网华为云Stack标准化配置华为云Stack配置概览华为云Stack云服务全视图华为云Stack部署方案节点类型说明华为云Stack云服务组件部署场景管理节点部署原则云平台管理规格华为云Stack IaaS场景&高阶场景起步必选部署组件x86…

Python高级语法----Python多线程与多进程

文章目录 多线程多进程注意事项多线程与多进程是提高程序性能的两种常见方法。在深入代码之前,让我们先用一个简单的比喻来理解它们。 想象你在一家餐厅里工作。如果你是一个服务员,同时负责多个桌子的顾客,这就类似于“多线程”——同一个人(程序)同时进行多项任务(线程…

RflySim | 滤波器设计实验二

本讲是关于无人机滤波器,其中包括无人机滤波器简介、测量原理、线性互补滤波器设计、线性互补滤波器参数分析、卡尔曼滤波器设计等。 滤波器设计实验2 卡尔曼滤波器是一种递推线性最小方差估计算法,它的最优估计需满足以下三个条件: 1&#…

【Spring】静态代理

例子: 租房子 角色: 我 (I ) 中介( Proxy ) 房东( host ) Rent 接口 package org.example;public interface Rent {void rent(); }房东 package org.example;public class Host implements Rent{Overridepublic void rent() …

NAT协议

目录 NAT 前言 NAT地址转换表 NAT分类 前言 静态NAT 192.168.1.2访问200.1.1.2执行过程 动态NAT 192.168.1.2访问200.1.1.2执行过程 NAPT 192.168.1.2的5000端口访问200.1.1.2的80端口执行过程 基本命令 配置动态NAPT转换 定义内外网接口 配置NAPT 静态NAPT配置…

NetworkManager 图形化配置 bond

1、在桌面右下角找到网络连接标识,鼠标右击,选择编辑连接,如下图 注意:此次示例使用 ens37 和 ens38 两张网卡组成 bond。在配置 bond 前为了网 络稳定如果子网卡已有网络连接的建议先删除 bond 子网卡的网络连接。 2、单击按钮&a…

Python高级语法----Python异步编程入门

文章目录 异步编程概念asyncio模块基础event loop和coroutineasync与await关键字代码示例结论在现代软件开发中,异步编程已经成为一个不可或缺的概念,尤其是在处理I/O密集型任务和高并发需求时。Python作为一门多范式编程语言,自3.5版本以来,通过引入asyncio模块和async/aw…

UseGalaxy.cn生信云平台文本文件操作手册

文本文件是生物信息学中应用非常广泛的文本格式,甚至可以说是最重要的文件格式,比如常见的测序下机数据Fastq、参考基因组保存格式Fasta、比对文件SAM,以及突变列表VCF,它们都是文本文件。熟练地进行文本文件的处理,对…

facebook分享-错误记录

无法拉起分享 "code":30000,"msg":"fail:API_ERROR: API_ERROR" 1.确认facebook的app_id是否一致 2.确认是否在app_id应用的白名单里,注册meta开发者,然后把主页的user_id给管理员加 A ContentProvider for this app was…

响应式生活常识新闻博客资讯网站模板源码带后台

模板信息: 模板编号:30483 模板编码:UTF8 模板分类:博客、文章、资讯、其他 适合行业:博客类企业 模板介绍: 本模板自带eyoucms内核,无需再下载eyou系统,原创设计、手工书写DIVCSS&a…

Pandas - 数据转换

数据转换一班包括一列数据转换为多列数据,行列转换,DataFrame转换为字典、DataFrame转换为列表和DataFrame转换为元组等。 1.一列数据转换为多列数据 如原始地址数据为:“广东省 深圳市 罗湖区 xxxx”, 此时如果我们需要按照省来…

解密网易数帆DataOps“三剑客”:从数据开发治理、指标中台到ChatBI

大数据产业创新服务媒体 ——聚焦数据 改变商业 近日,以“数智聚力,共赴新程”为主题的2023网易数字大会在杭州召开。在这次大会上,数据猿采访了网易副总裁、网易数帆总经理汪源,网易数帆大数据产品线总经理余利华,对…

centos7部署Canal与Canal集成使用

1、简介 canal [kə’nl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费 早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigge…