Spring循环依赖问题

【Spring源码三千问】哪些循环依赖问题Spring解决不了? 哪些循环依赖问题Spring解决不了?前言版本约定正文场景一: prototype 类型的循环依赖场景二: constructor 注入的循环依赖场景三:Proxy 类型的 Bean 的循环依赖小结 系列博文: 【老王读Spring IoC-0】Spring IoC 引入 【老王读Spring IoC-1】IoC 之控制反转引入 【老王读Spring IoC-2】IoC 之 BeanDefinition 扫描注册 【老王读Spring IoC-3】Spring bean 的创建过程 【老王读Spring I 阅读详情

Spring中单例Bean实例创建的三个步骤:

  1. 创建对象,createBeanInstance

  2. 填充属性,populateBean

  3. 初始化操作(调用初始化方法),initializeBean

构造器循环依赖

无法解决,因为实例都还没有创建出来。比如A构造器依赖B,B构造器依赖A,在创建A实例需要B实例,创建B实例又需要A实例,所以构造器循环依赖无法解决。

setter方法循环依赖

setter方法循环依赖发生在第二步,解决的方法是,在实例化之后、填充属性之前将Bean实例放入缓存

缓存

// 一级缓存,存储创建完全成功的单例Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 二级缓存,在Bean实例化、依赖注入、初始化后,会把Bean实例从三级缓存转移到二级缓存
// 如果需要生成代理对象,这里已经是代理对象了
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

// 三级缓存,主要来解决循环依赖问题,把刚实例化的对象封装成ObjectFactory,然后放入三级缓存
// 为什么封装成ObjectFactory
// 因为用直接的实例对象的引用的话,如果该实例对象后面会生成代理对象,就没办法做替换
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
三级缓存

AbstractAutowireCapableBeanFactory#doCreateBean方法中会调用DefaultSingletonBeanRegistry#addSingletonFactory把刚实例化的Bean对象封装成ObjectFactory对象放到三级缓存

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
    // 省略...
    // ObjectFactory实例
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    // 省略...
}


protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        /**
         * 如果单例池当中不存在才会add
         * 因为如果bean存在单例池的话其实已经是一个完整的bean了
         * 所以如果这个对象已经是一个完整的bean,就不需要关心,不需要进入if
         * 如果一级缓存中没有改实例,则放入三级缓存中
         */
        if (!this.singletonObjects.containsKey(beanName)) {
            // 放入三级缓存
            this.singletonFactories.put(beanName, singletonFactory);
            /**
             * 从二级缓存中remove掉当前的bean
             * 这三个map当中其实其实存的都是一个对象,不能同时存在三个map
             */
            // 从二级缓存中移除
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

用Lambda表达式封装成一个ObjectFactory对象,调用ObjectFactory#getObject时会调用getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                // aop的AspectJAwareAdvisorAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

调用AspectJAwareAdvisorAutoProxyCreator的父类AbstractAutoProxyCreator#getEarlyBeanReference方法

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    // 使用动态代理,产生代理对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}
二级缓存

在以下两个场景,会把Bean实例从三级缓存转移到二级缓存:

  1. 在Bean实例化、依赖注入、初始化后,AbstractAutowireCapableBeanFactory#doCreateBean方法中,调用DefaultSingletonBeanRegistry#getSingleton
  2. 获取Bean实例,会先去缓存中获取,AbstractBeanFactory#doGetBean方法中,调用DefaultSingletonBeanRegistry#getSingleton

DefaultSingletonBeanRegistry#getSingleton,把Bean实例从三级缓存转移到二级缓存。如果需要生成代理对象,二级缓存这里已经是代理对象,有两点:

  1. 代理对象是在初始化后的AbstractAutoProxyCreator#postProcessAfterInitialization方法生成。

  2. 如果对象提前暴露给其他Bean,singletonFactory.getObject()会产生代理对象

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存中获取单例对象
    Object singletonObject = this.singletonObjects.get(beanName);
    // isSingletonCurrentlyInCreation:判断当前单例bean是否正在创建中,也就是没有初始化完成
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 从二级缓存中获取单例bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            // allowEarlyReference:是否允许从singletonFactories中通过getObject拿到对象
            if (singletonObject == null && allowEarlyReference) {
                // 从三级缓存中获取单例bean
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 通过单例工厂获取单例bean
                    // 如果需要创建代理对象,这里会生成代理对象,调用AbstractAutoProxyCreator#getEarlyBeanReference方法
                    singletonObject = singletonFactory.getObject();
                    // 从三级缓存移动到了二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}
一级缓存

在对Bean实例化、依赖注入、初始化之后,会将Bean实例保存到一级缓存。

DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)方法中调用DefaultSingletonBeanRegistry#addSingleton将Bean实例保存到一级缓存,删除在二级和三级对应的。一级缓存是真正对外暴露的Bean实例对象缓存。

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
	    // 一级缓存
	    this.singletonObjects.put(beanName, singletonObject);
	    // 三级缓存
	    this.singletonFactories.remove(beanName);
	    // 二级缓存
	    this.earlySingletonObjects.remove(beanName);
	    // 已经注册的bean的名称集合
	    this.registeredSingletons.add(beanName);
    }
}

在依赖注入时,会调用getBean->doGetBean->getSingleton,会先从一级缓存取,一级没有去二级取,二级没有去三级取


为什么需要三级缓存

把实例化的bean通过封装成 ObjectFactory 放入第三级缓存做提前暴露,在DefaultSingletonBeanRegistry#getSingleton方法中调用ObjectFactory#getObject,当时放入三级缓存的是匿名内部类

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

调用了AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法,如果这里有用到aop,则会创建代理对象。所以二级缓存保存的是半成品的实例bean或者是代理对象(未进行依赖注入和初始化)。

这么看貌似两层缓存就可以解决循环依赖,为什么设计成三层缓存?

每次从第三级缓存获取(调用ObjectFactory#getObject)都会创建一个代理对象,但需要的是单例对象,这是不行的(比如一个类中有两个相同类型的属性,就会两次获取)。所以借助二级缓存,只需要从二级缓存中拿取,没必要再执行一遍ObjectFactory#getObject方法再产生一个新的代理对象,保证始终只有一个代理对象。

Spring循环依赖相关问题 Spring循环依赖相关问题-面试被问了记录下 阅读详情

相关推荐

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

什么是Spring中的三级缓存? Spring中的三级缓存是如何解决循环依赖问题的? 解决Spring循环依赖问题一定需要第三级缓存吗? 解决Spring循环依赖问题一定需要第二级缓存吗? 什么场景下的Spring循环依赖问题无法解决? 采用了构造器的注入方式 相互依赖的bean都是原型bean 采用@DependsOn注解而导致的循环依赖 使用了@Async注解 如何解决构造器注入的循环依赖? @Lazy是如何解决Spring循环依赖问题的?

weixin_43739821的博客 1610

Spring中的循环依赖问题

浅谈spring中的循环依赖问题

AG.『Feng』的博客 3792

【微服务】spring 循环依赖问题深入解析

spring 循环依赖问题深入解析

congge_study的博客 4057

如何解决循环依赖问题

‎在Spring中,有很多方法可以处理循环依赖关系。首先要考虑的是重新设计,这样就不需要循环依赖关系。‎‎但是如果项目中具有循环依赖项,则可以按照以上建议进行修改。

m0_75031618的博客 2809

基于深度学习的UWB门禁检测

针对基于UWB技术智能门禁管理系统,由于测距误差或非视距原因导致门禁产生不合理动作的现象。分析如何采用基于深度学习的方法,通过对TWR测距数据、TWR监听数据、到达角度差(PDOA)、信道脉冲响应(CIR)等数据维度进行深度学习,利用 TensorFlow建立相应的推导模型,对卡片姿态进行更加精准可靠的检测,从而更好完成闸机开合控制。

tenth_quadrant的博客 2027

SEMI E40标准的加工任务状态模型(Process Job State Model)解析

半导体制造设备的自动化控制离不开对加工任务(Process Job)的精细管理。SEMI E40标准定义了加工任务的全生命周期状态模型。分层建模,支持嵌套与并发子状态,确保复杂流程的灵活控制。SEMI E40通过。

bengg123的博客 1775

循环依赖问题及解决办法

循环依赖(Circular Dependency)是指两个或多个组件之间相互引用,形成一个环路的情况。在编程中,循环依赖通常被视为一种不良的实践,因为它可能导致代码的可维护性下降,以及运行时的问题。

qq_52337851的博客 3122

保姆级教程:在Ubuntu 22.04上用Conda搞定nuPlan数据集环境(Python 3.9 + PyTorch)

本文提供了一份详细的Ubuntu 22.04环境下使用Conda配置nuPlan自动驾驶数据集的保姆级教程,涵盖Python 3.9安装、Conda环境管理、PyTorch配置及数据集可视化验证等关键步骤,帮助开发者高效搭建开发环境。

weixin_29061997的博客 255

如何通过三级缓存解决 Spring 循环依赖

首先,什么是循环依赖?AService 和 BService 互相依赖:这个应该很好理解。

江南一点雨的专栏 1800

目标检测 RT-DETR(2023)详细解读

在使用了这个技巧后,显然训练过程中,类别的标签不再是此前的0和1离散值,而是0~1的连续值。关于 Query Selection(查询向量选择),大家应该并不陌生,这个方法可谓在DETR领域大杀四方,如DAB-DETR对查询向量进行重构理解,将其解释为Anchor,DN-DETR通过查询降噪来应对匈牙利匹配的二义性所导致的训练时间长的问题,DINO提出从Encoder中选择Top-k特征进行学习等一系列方法,这都无疑向我们证明,查询向量很重要,选择好的Query能够让我们事半功倍。在RT-DETR中,

qq_51352130的博客 5870

USB3.0/3.1信号完整性分析仿真

• 1.了解USB3.0通路的互连关系,解读原理图接口电路和PCB的互连电路。对USB布线的通路及阻抗进行检查和分析。 • 2.使用USB3.0实例文件提取互连线的S参数,选择SSRX+,SSRX-,SSTX+,SSTX-,D+,D-组的互连线,使用PowerSI,Clarity,3DFEM等工具提起关键网络的S参数。提取S参数以后需要分析S参数的互连特征,对S参数的结果开展评价。 • 3.使用SYSTEM SI USB3 Compliance工程向导模型,建立USB仿真实例框架,在框架BOLCK中带入S参

小猪配齐电子设计 1万+

如何解决循环依赖

如何解决循环依赖

weixin_69084736的博客 1万+

CANoe/CANalyzer新手必看:从零配置到报文分析的完整流程(附B站官方教程实操)

本文为CANoe/CANalyzer新手提供从零配置到报文分析的完整流程指南,涵盖硬件连接、工程配置、报文捕获及信号级分析等关键步骤。通过详细的实操案例和B站官方教程,帮助工程师快速掌握汽车电子总线分析工具的核心应用,提升诊断效率与通信系统理解。

y7z8a的博客 774

Spring如何解决循环依赖问题

1、什么是循环依赖:类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。 2、循环依赖问题在Spring中主要有三种情况: (1)通过构造方法进行依赖注入时产生的循环依赖问题。 (2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。 (3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。 3、Spring解决的单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过两个缓存来解决的

张维鹏的博客 4万+

Linux之SUSE系统SAP-HANA经常系统内存不足使得SAP应用不能使用解决方案

问题描述 Linux内存使用量超过阈值,使得sap应用程序无可用内存,最终导致程序崩溃。 问题原因 自己查阅了一些资料,也基于zabbix的可用内存和实际不太一样入手去找原因发现问题出现于Cached的值过大,导致系统没有可以再分配的内存空间。Cached只要用来缓存文件的,经常读写的文件会被缓存到Cached中,可以增加读写效率,该功能是Linux系统内核提供的,而SAP-HANA的日志文件很大...

liuxiangyang_的博客 6711

3.摄像头组件- CVP (ISP)

本文介绍 车载摄像头组件-ISP

2401_83458819的博客 1617

Spring的循环依赖问题

是指两个或多个Bean互相依赖,导致Spring在实例化它们时形成死循环。@Component@Autowired@Component@Autowired当Spring容器尝试创建A的实例时,发现需要B,则要去创建B;而创建B又需要A,最终就会导致依赖注入死循环。Spring 中的循环依赖问题是 Bean 生命周期管理中一个经典而复杂的问题。

动动发财的小手点点赞 1105

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Spring 如何解决循环依赖问题

在关于Spring的面试中,我们经常会被问到一个问题,就是Spring是如何解决循环依赖的问题的。 这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能够一下子思考出个中奥秘。 本文主要针对这个问题,从源码的角度对其实现原理进行讲解。 1、过程演示 关于Spring bean的创建,其本质上还是一个对象的创建,既然是对象,读者朋友一定要明白一点就是,一个完整的对象包含两部分:当前对象实例化和对象属性的实例化。 在Spring中,对象的实例化是通过反射实现的,而

CRMEB小程序商城的博客 2万+

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Spring 循环依赖问题详解

由于 ServiceA 尚未完成初始化,Spring 会将 ServiceA 的早期版本(部分初始化的对象)放入二级缓存(Early Singleton Cache),并使用这个早期版本来满足 ServiceB 的依赖。ServiceA 完成初始化后,Spring 确保 ServiceB 中的 ServiceA 引用也指向完全初始化的 ServiceA 实例。ServiceB 创建完成后,Spring 返回到 ServiceA 的创建过程,并用完整的 ServiceB 实例替换掉早期版本。

m0_73635284的博客 1654

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Spring循环依赖问题详解

在Spring框架的学习和使用中,循环依赖是一个比较常见的问题,尤其对于初学者来说,理解起来可能会有些困难。本文将深入浅出地讲解Spring中的循环依赖问题,包括其现象、原因、解决方案以及面试中可能涉及的相关问题,帮助大家更好地理解和掌握这一知识点。先解释循环依赖的基本概念,即两个或多个Bean互相依赖形成闭环。然后说明Spring中循环依赖的几种类型,包括构造器循环依赖、field属性注入循环依赖(单例和prototype)。

shaozheng0503的博客 1464

Spring Bean的循环依赖问题

A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。比如:丈夫类Husband,妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。

qq_52897007的博客 1191

【Spring】Spring是如何解决循环依赖问题的

不过要注意,构造器注入的循环依赖是无法解决的,因为构造器注入需要先完成实例化,而实例化的时候就需要依赖的Bean已经存在。需要提醒他们在遇到循环依赖时,检查异常信息,确定是哪种类型的循环依赖,以及Spring是否支持解决。总结一下,回答中需要涵盖:三级缓存机制、处理流程(实例化、提前暴露引用)、构造器注入和原型Bean的限制,以及如何避免循环依赖的建议。另外,Spring只能解决单例作用域的Bean的循环依赖。首先,我得回忆一下循环依赖的定义,就是两个或多个Bean相互依赖,比如A依赖B,B又依赖A。

weixin_45325628的博客 2152

Spring Bean循环依赖问题及解决

类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。举例来说,假设存在两个服务类A和服务类B,如果A通过依赖注入的方式引用了B,且B通过依赖注入的方式引用了A,那么A和B之间就存在循环依赖。推广来说,如果涉及多个类,也存在这种依赖关系,那么也是循环依赖问题。循环依赖问题比较严重,有时会影响服务启动,有时会导致死循环调用(如果线上环境出现循环调用,会导致程序进入死循环,然后服务崩溃,进而导致用户请求无法响应,造成生产事故),应引起足够的重视。

云原生Java Web领域技术博客 4256

Spring 是如何解决循环依赖问题的

1.循环依赖 首先我们要知道什么是Spring的循环依赖问题。假如我们有两个bean,A 和 B。他们的代码简单如下: @Bean public class A { @Autowire private B b; } @Bean public class B { @Autowire private A a; } 也就是需要在A中注入B,在B中注入A,那么Spring在创建A的时候会出现这种现象:创建A实例后,在依赖注入时需要B,然后就去创建B,这时候发现又需要...

linzherong的博客 9475

Spring中循环依赖问题

1、什么是Spring中的循环依赖 循环依赖就是循环引用,也就是两个或者两个以上的Bean相互持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。 2、Spring处理循环依赖的机制 无法解决的循环依赖问题: 单例Bean构造函数的循环依赖 prototype原型的循环依赖 (对与原型Bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx方法产生循环依赖,Spring都会直接报错处理BeanCurrentlyInCreationException) 构造器的循环依赖

weixin_41998947的博客 1795
上一篇: 把java项目打包成docker镜像
下一篇: MyBatis一级和二级缓存
我还能卷
博客等级 码龄4年 22粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值