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

iOS逆向实战:绕过SSL Pinning抓取ChatGPT设备令牌 在移动应用安全与网络协议分析领域,中间人攻击是一种核心的流量拦截与分析技术。其原理是在客户端与服务器之间插入一个代理节点,通过伪造数字证书来解密和窥探HTTPS加密流量。这项技术的工程价值在于,它使开发者能够调试API、分析通信协议以及实现自动化集成。然而,许多高安全要求的应用(如金融、社交类App)会采用SSL Pinning(证书绑定)技术来抵御此类攻击,这为逆向工程带来了挑战。本文聚焦于一个具体的应用场景:如何针对启用了SSL Pinning的ChatGPT iOS客户端,通过特定的工具链(如Trol 阅读详情

1、案发情况

@Service
public class TestService1 {

    @Autowired
    private TestService2 testService2;

    @Async
    public void test1() {
    }
}
@Service
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}

这两段代码中定义了两个Service类:TestService1和TestService2,在TestService1中注入了TestService2的实例,同时在TestService2中注入了TestService1的实例,这里构成了循环依赖。

只不过,这不是普通的循环依赖,因为TestService1的test1方法上加了一个@Async注解。

大家猜猜程序启动后运行结果会怎样?

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService1': Bean with name 'testService1' has been injected into other beans [testService2] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

报错了。。。原因是出现了循环依赖。

「不科学呀,spring不是号称能解决循环依赖问题吗,怎么还会出现?」

如果把上面的代码稍微调整一下:

@Service
public class TestService1 {

    @Autowired
    private TestService2 testService2;

    public void test1() {
    }
}

把TestService1的test1方法上的@Async注解去掉,TestService1和TestService2都需要注入对方的实例,同样构成了循环依赖。

但是重新启动项目,发现它能够正常运行。这又是为什么?

带着这两个问题,让我们一起开始spring循环依赖的探秘之旅。

2.什么是循环依赖?

循环依赖:说白是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用。

3.循环依赖的N种场景

spring中出现循环依赖主要有以下场景:

 

单例的setter注入

这种注入方式应该是spring用的最多的,代码如下:

@Service
public class TestService1 {

    @Autowired
    private TestService2 testService2;

    public void test1() {
    }
}
@Service
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}

这是一个经典的循环依赖,但是它能正常运行,得益于spring的内部机制,让我们根本无法感知它有问题,因为spring默默帮我们解决了。

spring内部有三级缓存:

  • singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例
  • earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例
  • singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。

下面用一张图告诉你,spring是如何解决循环依赖的:

  图1

细心的朋友可能会发现在这种场景中第二级缓存作用不大。

那么问题来了,为什么要用第二级缓存呢?

试想一下,如果出现以下这种情况,我们要如何处理?

@Service
public class TestService1 {

    @Autowired
    private TestService2 testService2;
    @Autowired
    private TestService3 testService3;

    public void test1() {
    }
}
@Service
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}
@Service
public class TestService3 {

    @Autowired
    private TestService1 testService1;

    public void test3() {
    }
}

TestService1依赖于TestService2和TestService3,而TestService2依赖于TestService1,同时TestService3也依赖于TestService1。

按照上图的流程可以把TestService1注入到TestService2,并且TestService1的实例是从第三级缓存中获取的。

假设不用第二级缓存,TestService1注入到TestService3的流程如图:

   图2

TestService1注入到TestService3又需要从第三级缓存中获取实例,而第三级缓存里保存的并非真正的实例对象,而是ObjectFactory对象。说白了,两次从三级缓存中获取都是ObjectFactory对象,而通过它创建的实例对象每次可能都不一样的。

这样不是有问题?

为了解决这个问题,spring引入的第二级缓存。上面图1其实TestService1对象的实例已经被添加到第二级缓存中了,而在TestService1注入到TestService3时,只用从第二级缓存中获取该对象即可。

 

  图3

 

还有个问题,第三级缓存中为什么要添加 ObjectFactory 对象,直接保存实例对象不行吗?

答:不行,因为假如你想对添加到三级缓存中的实例对象进行增强,直接用实例对象是行不通的。

针对这种场景spring是怎么做的呢?

答案就在 AbstractAutowireCapableBeanFactory 类 doCreateBean 方法的这段代码中:

 它定义了一个匿名内部类,通过 getEarlyBeanReference 方法获取代理对象,其实底层是通过AbstractAutoProxyCreator 类的 getEarlyBeanReference 生成代理对象。

多例的setter注入

这种注入方法偶然会有,特别是在多线程的场景下,具体代码如下:

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class TestService1 {

    @Autowired
    private TestService2 testService2;

    public void test1() {
    }
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}

 

很多人说这种情况 spring容器 启动会报错,其实是不对的,我非常负责任的告诉你程序能够正常启动。

为什么呢?

其实在 AbstractApplicationContext 类的 refresh 方法中告诉了我们答案,它会调用 finishBeanFactoryInitialization 方法,该方法的作用是为了spring容器启动的时候提前初始化一些bean。该方法的内部又调用了 preInstantiateSingletons 方法

标红的地方明显能够看出:非抽象、单例 并且非懒加载的类才能被提前初始bean。

而多例即SCOPE_PROTOTYPE类型的类,非单例,不会被提前初始化bean,所以程序能够正常启动。

如何让他提前初始化bean呢?

只需要再定义一个单例的类,在它里面注入TestService1

@Service
public class TestService3 {

    @Autowired
    private TestService1 testService1;
}

重新启动程序,执行结果:

Requested bean is currently in creation: Is there an unresolvable circular reference?

果然出现了循环依赖。

注意:这种循环依赖问题是无法解决的,因为它没有用缓存,每次都会生成一个新对象。

构造器注入

这种注入方式现在其实用的已经非常少了,但是我们还是有必要了解一下,看看如下代码:

@Service
public class TestService1 {

    public TestService1(TestService2 testService2) {
    }
}
@Service
public class TestService2 {

    public TestService2(TestService1 testService1) {
    }
}

运行结果:

Requested bean is currently in creation: Is there an unresolvable circular reference?

出现了循环依赖,为什么呢?

从图中的流程看出构造器注入没能添加到三级缓存,也没有使用缓存,所以也无法解决循环依赖问题。

单例的代理对象setter注入

这种注入方式其实也比较常用,比如平时使用:@Async注解的场景,会通过AOP自动生成代理对象。

我那位同事的问题也是这种情况。

@Service
public class TestService1 {

    @Autowired
    private TestService2 testService2;

    @Async
    public void test1() {
    }
}
@Service
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}

 从前面得知程序启动会报错,出现了循环依赖:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService1': Bean with name 'testService1' has been injected into other beans [testService2] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

为什么会循环依赖呢?

答案就在下面这张图中:

说白了,bean初始化完成之后,后面还有一步去检查:第二级缓存 和 原始对象 是否相等。由于它对前面流程来说无关紧要,所以前面的流程图中省略了,但是在这里是关键点,我们重点说说:

那位同事的问题正好是走到这段代码,发现第二级缓存 和 原始对象不相等,所以抛出了循环依赖的异常。

如果这时候把TestService1改个名字,改成:TestService6,其他的都不变。

@Service
publicclass TestService6 {

    @Autowired
    private TestService2 testService2;

    @Async
    public void test1() {
    }
}

what? 这又是为什么?

这就要从spring的bean加载顺序说起了,默认情况下,spring是按照文件完整路径递归查找的,按路径+文件名排序,排在前面的先加载。所以TestService1比TestService2先加载,而改了文件名称之后,TestService2比TestService6先加载。

为什么TestService2比TestService6先加载就没问题呢?

答案在下面这张图中:

这种情况testService6中其实第二级缓存是空的,不需要跟原始对象判断,所以不会抛出循环依赖。

DependsOn循环依赖

还有一种有些特殊的场景,比如我们需要在实例化Bean A之前,先实例化Bean B,这个时候就可以使用@DependsOn注解。

@DependsOn(value = "testService2")
@Service
public class TestService1 {

    @Autowired
    private TestService2 testService2;

    public void test1() {
    }
}
@DependsOn(value = "testService1")
@Service
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}

程序启动之后,执行结果:

Circular depends-on relationship between 'testService2' and 'testService1'

这个例子中本来如果 TestService1 和 TestService2 都没有加 @DependsOn 注解是没问题的,反而加了这个注解会出现循环依赖问题。

这又是为什么?

答案在 AbstractBeanFactory 类的 doGetBean 方法的这段代码中:

它会检查dependsOn的实例有没有循环依赖,如果有循环依赖则抛异常。

4.出现循环依赖如何解决?

项目中如果出现循环依赖问题,说明是spring默认无法解决的循环依赖,要看项目的打印日志,属于哪种循环依赖。目前包含下面几种情况

生成代理对象产生的循环依赖

这类循环依赖问题解决方法很多,主要有:

  1. 使用@Lazy注解,延迟加载
  2. 使用@DependsOn注解,指定加载先后关系
  3. 修改文件名称,改变循环依赖类的加载顺序

使用@DependsOn产生的循环依赖

这类循环依赖问题要找到@DependsOn注解循环依赖的地方,迫使它不循环依赖就可以解决问题。

多例循环依赖

这类循环依赖问题可以通过把bean改成单例的解决。

构造器循环依赖

这类循环依赖问题可以通过使用@Lazy注解解决。

 

大中型超市收银系统(可直接运行) 光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开 立即下载

相关推荐

gitlab远程子仓库配置SSH - 保姆级教程

在网上搜索了一些方法,但是不够详细,走了好多弯路,所以记录下配置SSH的详细过程吧。

是菠萝的博客 1万+

Spring如何解决循环依赖

我们先来看看官方文档的说法:通俗来讲,就是A依赖B或者B依赖A,或者C依赖自己本身,或是三个以上,例如A依赖B,B依赖C,C又依赖A。

m0_46420244的博客 1万+

从宁德新能源面试官视角,拆解Halcon/OpenCV图像处理工程师的硬核技能树(附避坑指南)

本文从宁德新能源面试官视角出发,详细拆解Halcon/OpenCV图像处理工程师的核心技能树,涵盖硬件选型、算法实现和系统工程思维三大能力维度。文章提供工业视觉工程师面试的实战避坑指南,包括硬件配置要点、算法优化技巧及职业发展路径,助力求职者系统提升图像处理专业技能。

weixin_28732953的博客 129

spring如何解决循环依赖

本文将从spring如何解决循环依赖,以及那些情况下的循环依赖无法解决来分析,由于笔者水平有限,若有错误的地方望各位纠正并讨论。 一、循环引用示列 众所周知再spring中单列bean是支持循环依赖的,下面看代码: A.class代码: @Component public class A { @Autowired private B b; public A(){ System.out.println("init A"); } } A中注入了一个B; B.class代码.

weixin_43716742的博客 1万+

深信服上网行为管理设备登录方法、恢复出厂、恢复控制台密码

1.初识设备 1.1 设备外观 1.2 软件版本 2.如何登录设备 首次拿到AC/SG设备时,可以通过以下方法登录设备控制台 SANGFOR AC/SG设备,各网口默认的出厂IP为: eth0(LAN):10.251.251.251/24 , eth1(DMZ)10.252.252.252/24 1、使用一根交叉线连接设备和电脑 (1) 如果电脑连接的是设备的eth0口,需先在电脑上配...

✍千里之行,始于足下 ^,^ 5万+

避坑指南:Livox Mid-360双雷达SLAM配置中,那些官方文档没说的细节(IP设置、话题分离与时间同步)

本文详细解析了Livox Mid-360双雷达SLAM配置中的关键优化点,包括IP设置、话题分离与时间同步等官方文档未提及的细节。通过实战案例和代码示例,帮助开发者解决点云错位、IMU数据混乱等问题,提升SLAM系统的稳定性和精度。特别针对网络架构设计、驱动修改和时间同步机制提供了专业指导。

weixin_29745609的博客 426

mysql循环依赖_spring:我是如何解决循环依赖的?

它1.由同事抛的一个问题开始最近项目组的一个同事遇到了一个问题,问我的意见,一下子引起的我的兴趣,因为这个问题我也是第一次遇到。平时自认为对spring循环依赖问题还是比较了解的,直到遇到这个和后面的几个问题后,重新刷新了我的认识。我们先看看当时出问题的代码片段:@Servicepublic class TestService1{@Autowiredprivate TestService2 tes...

weixin_39641697的博客 583

树根互联Java开发面试题及参考答案

说一下 Java 集合类。 Java 集合类是用于存储和操作一组对象的容器。它提供了多种数据结构,方便程序员对数据进行高效的管理和处理。 Java 集合框架主要包括两大接口:Collection 和 Map。 Collection 接口是一组对象的集合,它有三个主要的子接口:List、Set 和 Queue。 List 接口代表有序的集合,可以包含重复的元素。例如 ArrayList,它是基于数组实现的动态数组。它的优点是随机访问效率高,通过索引访问元素的时间复杂度为 O (1)。当

大模型大数据攻城狮的专栏 856

第六篇 再读Spring 之 循环依赖处理

文章目录前言一、关于循环依赖1. 什么是循环依赖2. 具体有哪几种文件级别触发方式识别处理对象级别二、1.引入库2.读入数据总结 前言 本文就Spring中循环依赖的场景和处理方式做汇总和探讨。 一、关于循环依赖 1. 什么是循环依赖 举个栗子,二元关系中A, B之间的循环依赖 使用数据结构中的图来抽象描述下,就是是一个有向有环图。基于有向有环的原则,我们可以轻松构造出4元关系,5元关系,甚至N元关系下的循环依赖。 假设每个节点代表任务,依赖标识任务完成的前后顺序,这张图就是一个任务调度图,大数据.

技术进步那些的事 1510

【Unity的HDRP渲染管线下实现好用的GUI模糊和外描边流光效果_Blur_OutLine_案例分享(内附源码)】

因为没有用到纹理,但介于是UI层,没有MainTeX的时候,会提示报错,所以这里加上了MainTex,最后不在片段着色渲染出来既可。这样UI层级显示正常了,但旋转后的模糊效果,跟背景拼接不上,有很大的偏移。猜测是屏幕UV采样错误导致,只要找到屏幕UV数值,更正过来就可以了,RenderTexture设置一下Size既可,其他的先不管。没有贴图,用Shader实现外描边流光效果。将主相机拖入Render Camera。新建一个RenderTexture。

暴走约伯的博客 1149

(十五)CX20106A 中超声波测量的应用

(十五)CX20106A 中超声波测量的应用

m0_52555663的博客 4514

Spring解决循环依赖的3种方法

如果大家理解开头那句话的话,这个报错应该不惊讶,Spring容器先创建单例StudentA,StudentA依赖StudentB,然后将A放在“当前创建Bean池”中,此时创建StudentB,StudentB依赖StudentC ,然后将B放在“当前创建Bean池”中,此时创建StudentC,StudentC又依赖StudentA, 但是,此时Student已经在池中,所以会报错,,因为在池中的Bean都是未初始化完的,所以会依赖错误 ,(初始化完的Bean会从池中移除)

xiaoweids的博客 2748

滚雪球学Java(01):Java是什么?| 入门指南

🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!!

**My Coding Family** 629

【Spring】Spring的循环依赖以及解决方案

​ Spring循环依赖指的是两个或多个Bean之间相互依赖,形成一个环状依赖的情况。通俗的说,就是A依赖B,B依赖C,C依赖A,这样就形成了一个循环依赖的环。​ Spring循环依赖通常会导致Bean无法正确地被实例化,从而导致应用程序无法正常启动或者出现异常。因此,Spring循环依赖是一种需要尽量避免的情况。

长夜烛火的博客 7996

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Spring三级缓存解决循环依赖

1. 前言 循环依赖:就是N个类循环(嵌套)引用。 通俗的讲就是N个Bean互相引用对方,最终形成闭环。用一副经典的图示可以表示成这样(A、B、C都代表对象,虚线代表引用关系): 其实可以N=1,也就是极限情况的循环依赖:自己依赖自己 这里指的循环引用不是方法之间的循环调用,而是对象的相互依赖关系。(方法之间循环调用若有出口也是能够正常work的) 2. Spring Bean的...

傅红雪的专栏 2万+

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Spring如何解决循环依赖问题

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

张维鹏的博客 4万+

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Spring是如何解决循环依赖的

循环依赖:一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用。第一种情况:自己依赖自己的直接依赖第二种情况:两个对象之间的直接依赖第三种情况:多个对象之间的间接依赖Spring 处理循环依赖的方式:有构造器注入,不一定会产生问题,具体得看是否都是构造器注和 BeanName 的字母序;如果单纯为了打破循环依赖,不需要三级缓存,两级就够了;三级缓存是否为延迟代理的创建,尽量不打破 Bean 的生命周期。

zhzjn的博客 909

【Spring】spring中怎么解决循环依赖的问题

在软件开发中,依赖注入是一种常见的设计模式,它可以帮助我们管理类之间的依赖关系,提高代码的可维护性和灵活性。然而,有时候我们可能会遇到循环依赖的问题,即两个或多个类之间相互依赖,形成一个循环引用链。Spring框架作为一个优秀的依赖注入容器,提供了多种解决循环依赖问题的机制,本文将对这些机制进行详细讲解。循环依赖是软件开发中常见的问题之一,但通过合理的设计和使用Spring框架提供的解决方案,我们可以有效地避免或解决这些问题。

AliceNo的博客 1700

spring是如何解决循环依赖的呢?

1、什么是循环依赖? 循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方。 A类中有一个B类型的成员变量,需要注入B B类中有一个A类型的成员变量,需要注入A 1、循环依赖的场景 1.1、构造器的循环依赖 //A类 public Class A{ private B b; pubcli A(B b){ this.b=b; } } //B类 public Class B{ private A a; public B(A a){

Mark 1万+

Spring 是怎么解决循环依赖的

循环依赖,简单来说就是多个 Bean 之间相互依赖,形成一个闭环。例如,Bean A 依赖 Bean B,Bean B 又依赖 Bean C,而 Bean C 反过来又依赖 Bean A,这样就构成了循环依赖。在 Spring 容器创建 Bean 的过程中,如果遇到循环依赖,若不进行特殊处理,就会导致创建 Bean 的过程陷入死循环,无法成功初始化 Bean。​根据依赖注入方式的不同,循环依赖主要分为三种类型:​构造器循环依赖:通过构造函数进行依赖注入时产生的循环依赖。

qq_74181189的博客 1135

【JavaWeb】Spring 如何解决循环依赖

循环依赖:一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用。第一种情况:自己依赖自己的直接依赖第二种情况:两个对象之间的直接依赖第三种情况:多个对象之间的间接依赖Spring 处理循环依赖的方式:有构造器注入,不一定会产生问题,具体得看是否都是构造器注和 BeanName 的字母序;如果单纯为了打破循环依赖,不需要三级缓存,两级就够了;三级缓存是否为延迟代理的创建,尽量不打破 Bean 的生命周期。

精益求精 1307

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

循环依赖是指两个或多个Bean之间形成相互依赖的闭环。直接循环依赖:Bean A依赖Bean B,Bean B也依赖Bean A。间接循环依赖:Bean A依赖Bean B,Bean B依赖Bean C,Bean C又依赖Bean A。自我依赖:Bean A的字段或构造方法中直接注入自身实例。当Spring容器尝试初始化这些Bean时,会因无法确定创建顺序而抛出BeanCurrentlyInCreationException异常。

lixianchuang的博客 1144

Spring 如何解决循环依赖以及那些无法解决的循环依赖

循环依赖,简单来说,就是两个或两个以上的 bean 相互持有对方的引用,最终形成一个闭环。比如,A bean 依赖 B bean,而 B bean 又依赖 A bean,这就像两个人互相拉着对方的手,谁也没办法先迈出第一步。在 Spring 中,注入 bean 的方式主要有构造器注入和 field 属性注入,不同的注入方式在面对循环依赖时表现各异。Spring 通过三级缓存机制巧妙地解决了 field 属性注入的循环依赖问题,同时借助动态代理(@Scope的proxyMode属性和@Lazy。

lrh35的博客 1087
上一篇: 微信 JSAPI 支付流程
下一篇: 校验使用javax.validation
MMP菜的一匹
博客等级 码龄10年 15粉丝 21原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值