广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >spring中如何解决循环依赖
  • 383
分享到

spring中如何解决循环依赖

2024-04-02 19:04:59 383人浏览 薄情痞子
摘要

这期内容当中小编将会给大家带来有关spring中如何解决循环依赖,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.由同事抛的一个问题开始我们先看看当时出问题的代码片段:@

这期内容当中小编将会给大家带来有关spring中如何解决循环依赖,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

1.由同事抛的一个问题开始

我们先看看当时出问题的代码片段:

  1. @Service 

  2. public class TestService1 { 

  3.  

  4.     @Autowired 

  5.     private TestService2 testService2; 

  6.  

  7.     @Async 

  8.     public void test1() { 

  9.     } 


@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.什么是循环依赖?

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

第一种情况:自己依赖自己的直接依赖

spring中如何解决循环依赖

第二种情况:两个对象之间的直接依赖

spring中如何解决循环依赖

第三种情况:多个对象之间的间接依赖

spring中如何解决循环依赖

前面两种情况的直接循环依赖比较直观,非常好识别,但是第三种间接循环依赖的情况有时候因为业务代码调用层级很深,不容易识别出来。

3.循环依赖的N种场景

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

spring中如何解决循环依赖

单例的setter注入

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

  1. @Service 

  2. public class TestService1 { 

  3.  

  4.     @Autowired 

  5.     private TestService2 testService2; 

  6.  

  7.     public void test1() { 

  8.     } 


@Service public class TestService2 {      @Autowired     private TestService1 testService1;      public void test2() {     } }

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

spring内部有三级缓存

  • singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例

  • earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例

  • singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。

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

spring中如何解决循环依赖

图1

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

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

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

  1. @Service 

  2. public class TestService1 { 

  3.  

  4.     @Autowired 

  5.     private TestService2 testService2; 

  6.     @Autowired 

  7.     private TestService3 testService3; 

  8.  

  9.     public void test1() { 

  10.     } 


  1. @Service 

  2. public class TestService2 { 

  3.  

  4.     @Autowired 

  5.     private TestService1 testService1; 

  6.  

  7.     public void test2() { 

  8.     } 


@Service public class TestService3 {      @Autowired     private TestService1 testService1;      public void test3() {     } }

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

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

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

spring中如何解决循环依赖

图2

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

这样不是有问题?

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

spring中如何解决循环依赖

图3

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

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

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

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

spring中如何解决循环依赖

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

多例的setter注入

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

  1. @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 

  2. @Service 

  3. public class TestService1 { 

  4.  

  5.     @Autowired 

  6.     private TestService2 testService2; 

  7.  

  8.     public void test1() { 

  9.     } 


@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Service public class TestService2 {      @Autowired     private TestService1 testService1;      public void test2() {     } }

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

为什么呢?

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

spring中如何解决循环依赖

标红的地方明显能够看出:非抽象、单例 并且非懒加载的类才能被提前初始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?

果然出现了循环依赖。

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

构造器注入

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

  1. @Service 

  2. public class TestService1 { 

  3.  

  4.     public TestService1(TestService2 testService2) { 

  5.     } 


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

运行结果:

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

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

spring中如何解决循环依赖

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

单例的代理对象setter注入

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

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

  1. @Service 

  2. public class TestService1 { 

  3.  

  4.     @Autowired 

  5.     private TestService2 testService2; 

  6.  

  7.     @Async 

  8.     public void test1() { 

  9.     } 


@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.

为什么会循环依赖呢?

答案就在下面这张图中:

spring中如何解决循环依赖

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

spring中如何解决循环依赖

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

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

@Service publicclass TestService6 {      @Autowired     private TestService2 testService2;      @Async     public void test1() {     } }

再重新启动一下程序,神奇般的好了。

what? 这又是为什么?

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

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

答案在下面这张图中:

spring中如何解决循环依赖

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

DependsOn循环依赖

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

  1. @DependsOn(value = "testService2") 

  2. @Service 

  3. public class TestService1 { 

  4.  

  5.     @Autowired 

  6.     private TestService2 testService2; 

  7.  

  8.     public void test1() { 

  9.     } 


@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方法的这段代码中:

spring中如何解决循环依赖

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

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

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

spring中如何解决循环依赖

上述就是小编为大家分享的spring中如何解决循环依赖了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网JavaScript频道。

--结束END--

本文标题: spring中如何解决循环依赖

本文链接: https://www.lsjlt.com/news/77727.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • spring中如何解决循环依赖
    这期内容当中小编将会给大家带来有关spring中如何解决循环依赖,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.由同事抛的一个问题开始我们先看看当时出问题的代码片段:@...
    99+
    2022-10-19
  • Spring怎么解决循环依赖
    本篇内容介绍了“Spring怎么解决循环依赖”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!写在前面最近,在...
    99+
    2022-10-19
  • Spring轻松解决循环依赖
    目录解决循环依赖的原理源码解析总结Spring 框架是一个流行的Java应用程序框架,它提供了许多强大的功能,如依赖注入和面向切面编程。然而在使用 Spring 框架时,我们可能会遇...
    99+
    2023-05-16
    Spring循环依赖怎么解决 Spring循环依赖
  • 如何解决Spring循环依赖问题
    本文小编为大家详细介绍“如何解决Spring循环依赖问题”,内容详细,步骤清晰,细节处理妥当,希望这篇“如何解决Spring循环依赖问题”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。公共代码package&nbs...
    99+
    2023-07-02
  • 如何解决spring检测循环依赖
    今天就跟大家聊聊有关检测循环怎么用,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。今天为CodeTop补充的题目是检测循环依赖。循环依赖检测。如,[[...
    99+
    2022-10-19
  • spring循环依赖问题如何解决
    本篇内容介绍了“spring循环依赖问题如何解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、三种循环依赖的情况①构造器的循环依赖:这种...
    99+
    2023-07-02
  • Spring循环依赖产生与解决
    目录循环依赖产生情景Spring如何解决循环依赖循环依赖产生情景 探讨如何解决循环依赖之前,更应该思考清楚什么情况下会发生这种问题? 1、模拟Prototype Bean的循环依赖 ...
    99+
    2022-12-20
    Spring如何解决循环依赖 Spring循环依赖
  • Spring循环依赖的解决方法
    这篇文章主要介绍Spring循环依赖的解决方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!循环依赖其实就是循环引用,很多地方都说需要两个或则两个以上的bean互相持有对方最终形成闭环才是循环依赖,比如A依赖于B,B...
    99+
    2023-06-06
  • Spring Boot循环依赖怎么解决
    本文小编为大家详细介绍“Spring Boot循环依赖怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“Spring Boot循环依赖怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧...
    99+
    2023-07-05
  • Spring中的循环依赖
    目录 一、什么是循环依赖?二、Bean的生命周期2.1 Spring Bean 的生命周期2.2 Bean 的生成步骤 三、三级缓存3.1三个缓存分别有什么作用 四、思路分析4.1 为什么 Spring 中还需要 singl...
    99+
    2023-08-16
    spring java 后端
  • Java Spring 循环依赖解析
    目录1、常见问题2、什么是循环依赖?3、循环依赖说明4、BeanCurrentlyInCreationException5、依赖注入的两种方式方式一:构造器方式注入依赖方式二:以 s...
    99+
    2022-11-13
  • 怎么解决Spring循环依赖问题
    本篇内容介绍了“怎么解决Spring循环依赖问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言循环依赖...
    99+
    2022-10-19
  • Spring三级缓存解决循环依赖
    目录一级缓存为什么不能在实例化A之后就放入Map?二级缓存 二级缓存已然解决了循环依赖问题,为什么还需要三级缓存?三级缓存 源码 我们都知道Spring中的BeanFactory是一...
    99+
    2022-11-12
  • Spring循环依赖问题怎么解决
    在Spring中,循环依赖问题是指两个或多个bean之间出现相互依赖的情况。由于Spring容器默认使用单例模式管理bean,因此循...
    99+
    2023-08-31
    Spring
  • Spring循环依赖的解决方案详解
    目录简介方案1. Feild注入单例(@AutoWired)方案2. 构造器注入+@Lazy方案3. Setter/Field注入单例方案4. @PostConstruct方案5. ...
    99+
    2022-11-13
  • java循环依赖如何解决
    在Java中,循环依赖是指两个或多个类之间存在相互依赖关系,导致无法编译或运行的情况。解决循环依赖的方法可以有以下几种: 重构代...
    99+
    2023-10-23
    java
  • 怎么在spring中解决循环依赖问题
    怎么在spring中解决循环依赖问题?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。setter singleton循环依赖使用SingleSetterBeanA依赖Sing...
    99+
    2023-06-08
  • Spring解决循环依赖的示例分析
    这篇文章主要介绍Spring解决循环依赖的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!  这里我们先借用一张图来通过视觉感受一下,看图:    其实,通过上面图片我想你应该能看图说话了,所谓的循环依赖其实就...
    99+
    2023-06-25
  • spring循环依赖策略解析
    循环依赖所谓循环依赖就是多个Bean之间依赖关系形成一个闭环,例如A->B->C->...->A 这种情况,当然,最简单的循环依赖就是2个Bean之间互相依赖:A->B(A依赖B), B->A(B依赖A)...
    99+
    2023-05-31
    spring 循环 依赖策略
  • 怎么理解Spring循环依赖
    本篇内容介绍了“怎么理解Spring循环依赖”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!通常来说,如果问Spring内部如何解决循环依赖,...
    99+
    2023-06-16
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作