目录

  • Spring中依赖注入的方式
  • 循环依赖的种类
  • Spring能解决哪些循环依赖
  • 单例模式下的构造器注入循环依赖问题Spring无法解决
  • 单例模式下的接口注入循环依赖问题Spring能解决
  • 单例模式下的setter注入循环依赖问题Spring能解决
  • 多例模式下的循环依赖Spring无法解决
  • Spring怎么解决的单例模式下的setter方法依赖注入引起的循环依赖问题
    • 解决循环依赖的流程
    • Spring为什么不能解决构造器的循环依赖?
    • Spring为什么不能解决多例的循环依赖?
    • Spring为什么使用三级缓存而不是二级缓存?

Spring中依赖注入的方式

1、构造器注入
2、setter方法注入
3、接口注入

循环依赖的种类

1、多例模式下的循环注入
2、单例模式下的setter注入
3、单例模式下的构造器注入

Spring能解决哪些循环依赖

多例模式下的循环注入,会导致无限创建对象,最终导致OOM,无法解决
Spring只能解决单例模式下的setter注入导致的循环依赖
单例模式下的构造器注入导致的循环依赖,在实例化对象时就出问题了,先实例化A,还是先实例化B,无法解决。

单例模式下的构造器注入循环依赖问题Spring无法解决

这种情况Spring无法解决

@Component
public class ClassA {private ClassB classB;@Autowiredpublic ClassA(ClassB classB) {this.classB = classB;}public ClassB getClassB() {return classB;}public void setClassB(ClassB classB) {this.classB = classB;}
}
@Component
public class ClassB {private ClassA classA;@Autowiredpublic ClassB(ClassA classA) {this.classA = classA;}public ClassA getClassA() {return classA;}public void setClassA(ClassA classA) {this.classA = classA;}}

报错信息

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-06-13 21:06:57.115 ERROR 11652 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : ***************************
APPLICATION FAILED TO START
***************************Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐
|  classA defined in file [F:\javaProject\githubProject\spring-demo\target\classes\com\spring\springdemo\service\ClassA.class]
↑     ↓
|  classB defined in file [F:\javaProject\githubProject\spring-demo\target\classes\com\spring\springdemo\service\ClassB.class]
└─────┘Process finished with exit code 1

单例模式下的接口注入循环依赖问题Spring能解决

@Component
public class ClassA {@Autowiredprivate ClassB classB;public ClassB getClassB() {return classB;}public void setClassB(ClassB classB) {this.classB = classB;}
}
@Component
public class ClassB {@Autowiredprivate ClassA classA;public ClassA getClassA() {return classA;}public void setClassA(ClassA classA) {this.classA = classA;}}

单例模式下的setter注入循环依赖问题Spring能解决

@Component
public class ClassA {private ClassB classB;public ClassB getClassB() {return classB;}@Autowiredpublic void setClassB(ClassB classB) {this.classB = classB;}
}
@Component
public class ClassB {private ClassA classA;public ClassA getClassA() {return classA;}@Autowiredpublic void setClassA(ClassA classA) {this.classA = classA;}
}

多例模式下的循环依赖Spring无法解决

VM参数设置堆大小为一个较小值:-Xmx5m

@Component
@Scope("prototype")
public class ClassA {private ClassB classB;public ClassB getClassB() {return classB;}@Autowiredpublic void setClassB(ClassB classB) {this.classB = classB;}
}
@Component
@Scope("prototype")
public class ClassB {private ClassA classA;public ClassA getClassA() {return classA;}@Autowiredpublic void setClassA(ClassA classA) {this.classA = classA;}
}

报错信息如下,这个错误是由于JVM花费太长时间执行GC且只能回收很少的堆内存时抛出的,因为创建了大量对象,而这些对象互相持有导致不能被GC回收。

Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: GC overhead limit exceededat java.lang.Class.getDeclaredMethods0(Native Method)at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)at java.lang.Class.getDeclaredMethod(Class.java:2128)at java.io.ObjectStreamClass.getPrivateMethod(ObjectStreamClass.java:1629)at java.io.ObjectStreamClass.access$1700(ObjectStreamClass.java:79)at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:520)at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:494)at java.security.AccessController.doPrivileged(Native Method)at java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:494)at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:391)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1134)at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441)at java.lang.Throwable.writeObject(Throwable.java:985)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1140)at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:396)at sun.rmi.transport.Transport$1.run(Transport.java:200)at sun.rmi.transport.Transport$1.run(Transport.java:197)at java.security.AccessController.doPrivileged(Native Method)at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
2021-06-13 21:31:41.137  WARN 4956 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.spring.springdemo.SpringDemoApplication]; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded
2021-06-13 21:31:41.287  INFO 4956 --- [           main] ConditionEvaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-06-13 21:31:41.585 ERROR 4956 --- [           main] o.s.boot.SpringApplication               : Application run failedorg.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.spring.springdemo.SpringDemoApplication]; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceededat org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:610) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.annotation.ConfigurationClassParser.access$800(ConfigurationClassParser.java:111) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1(ConfigurationClassParser.java:812) ~[spring-context-5.3.7.jar:5.3.7]at java.util.ArrayList.forEach(ArrayList.java:1257) ~[na:1.8.0_211]at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:809) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:780) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:193) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.7.jar:5.3.7]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.0.jar:2.5.0]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) [spring-boot-2.5.0.jar:2.5.0]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438) [spring-boot-2.5.0.jar:2.5.0]at org.springframework.boot.SpringApplication.run(SpringApplication.java:337) [spring-boot-2.5.0.jar:2.5.0]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336) [spring-boot-2.5.0.jar:2.5.0]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325) [spring-boot-2.5.0.jar:2.5.0]at com.spring.springdemo.SpringDemoApplication.main(SpringDemoApplication.java:10) [classes/:na]
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceededProcess finished with exit code 1

Spring怎么解决的单例模式下的setter方法依赖注入引起的循环依赖问题

首先回顾一下上集Spring源码之IoC,单例Bean添加到一、二、三级缓存的时机,源码解读

得到如下结论:

添加到一级缓存:完成Bean的实例化,依赖注入,初始化等操作,生成完整的单例Bean后进行添加。

添加到二级缓存:通过getBean获取实例,在三级缓存中获取到时,添加到二级缓存,并清理三级缓存。

添加到三级缓存:通过createBeanInstance实例化Bean之后,调用populateBean进行依赖注入之前。

解决循环依赖的流程

当ClassA和ClassB相互依赖时,Spring按照如下流程进行依赖注入,循环依赖问题的解决依赖于三级缓存。

1、通过getBean获取ClassA,缓存中不存在ClassA,首先实例化ClassA,并把ClassA加入三级缓存池,对ClassA进行依赖注入时发现ClassA依赖ClassB。

2、通过getBean获取ClassB,缓存中不存在ClassB,首先实例化ClassB,并把ClassB加入三级缓存池,对ClassB进行依赖注入时发现ClassB依赖ClassA。

3、通过getBean获取ClassA,缓存中存在ClassA,直接返回ClassA的引用。

4、ClassB获得ClassA的引用后完成依赖注入、初始化等操作。返回ClassB对象的引用。

5、ClassA获得ClassB的引用后完成依赖注入、初始化等操作。返回ClassA对象的引用。

6、至此,ClassA和ClassB完成了对象的创建,解决了循环依赖问题。

Spring为什么不能解决构造器的循环依赖?

因为加入缓存的顺序为:三级——>二级——>一级

而加入三级缓存是在实例化Bean后进行的,实例化Bean需要调用构造器,这就要求实例化ClassA的时候,缓存中已经存在ClassB,反过来实例化ClassB的时候,缓存中已经存在ClassA,这是不可能的,所以无法解决构造器循环依赖。

Spring为什么不能解决多例的循环依赖?

多例对象不会放入缓存池,所以必然无法从缓存池中获得对象,而是每次都新建一个对象。
当新建ClassA1对象时,发现依赖ClassB,则新建ClassB1,
新建ClassB1对象时,ClassB1发现依赖ClassA,则又新建一个ClassA2,
当新建ClassA2对象时,发现依赖ClassB,则新建ClassB2,
如此无限循环,创建大量对象,最终会导致OOM内存溢出。

Spring为什么使用三级缓存而不是二级缓存?

参考:https://www.cnblogs.com/semi-sub/p/13548479.html

Spring循环依赖问题,循环依赖的情况,能解决的情况,怎么解决的相关推荐

  1. Spring源码分析-循环依赖

    导语   前面提到了实例化Bean其实是一个复杂的过程,而在这个过程中比较难以理解的就是循环依赖的问题,下面就先来看看什么是循环依赖 文章目录 什么是循环依赖? Spring 是怎么解决循环依赖的? ...

  2. Spring解决bean之间的循环依赖(循环引用)

    2.概述 bean的实例化仅仅是获得了bean的实例,该bean仍在继续创建之中,之后在该bean实例的基础之上,还要做很多额外的操作,例如bean的属性填充.处理器的应用.bean的循环依赖解决等, ...

  3. 【Spring面试题】循环依赖如何解决?

    1.什么是循环依赖 循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环. 2.循环依赖的两种情况及其解决方案 2.1 构造器的循环依赖 这 ...

  4. 【Spring源码:循环依赖】一文弄懂Spring循环依赖

    1. 什么是循坏依赖 很简单,其实就是互相依赖对方,比如,有一个A对象依赖了B对象,B对象又依赖了A对象. // A依赖了B public class A{private B b; }// B依赖了A ...

  5. Spring面试题之循环依赖与三级缓存

    Spring的循环依赖问题 循环依赖图形说明: 循环依赖文字说明: 循坏依赖问题产生的原因是,bean对象的创建实际上是细分为实例化,属性填充,初始化.创建A对象的时候,先实例化A对象,b = nul ...

  6. java 注入 循环_spring依赖注入——循环依赖

    上一篇博客简单地分析了下依赖注入.但是对于依赖注入的很多细节,都没有深入的分析.这一篇博客会继续分析spring的依赖注入.这篇博客会解决分析getBean缓存时候遗留下来的循环依赖问题. 循环依赖分 ...

  7. java中友元类_友元类成员的依赖关系|循环依赖

    定义一个CBottle类,另一个类CCarton的某个成员对CBottle进行操作,因此在CBottle类中赋予CCarton成员的友元权利.我们很容易写出如下代码: //CBottle类的头文件 b ...

  8. Unity3d+GameFramework:资源分析,资源依赖,循环依赖检测

    资源依赖 先生成Resource 根据ResourceCollection.xml Resource resource = Resource.Create(name, variant, fileSys ...

  9. Spring源码系列:依赖注入(二)createBean

    在Spring源码系列:依赖注入(一)(AbstractBeanFactory-getBean)最后说道getBean是依赖注入的起点,bean的创建都是通过createBean来完成具体的创建的.c ...

最新文章

  1. MXNet半精度(FP16)
  2. 朱兴杰(1986-),男,泰康保险集团股份有限公司数据信息中心应用创新高级工程师...
  3. B00006 函数itoa()
  4. Atitit 提升记忆效率 有损压缩原理总结 目录 1. 常见方法 1 1.1. 抽象化提升一层 概念化 1 1.2. 骨架 ,目录化 大纲化 归纳整理 1 1.3. 提取关键词 ,摘要 ,
  5. QQ空间 1314学习网的日志 神奇的图片
  6. java中ArrayList小案例(快敲20遍++)
  7. element-ul基本使用
  8. java实现基金浮动_Java: 实现自回归分析/线性回归分析/基金各项指标计算等
  9. 22个优秀的橙色网页设计作品欣赏
  10. leetcode系列-链表
  11. 哈工大教授车万翔:自然语言处理中的伪数据
  12. S32DS中.ld(链接)文件学习
  13. 什么是IT咨询?IT外包又是什么?
  14. Altium Designer初学教程(一)
  15. Echarts- 饼图透明色效果图
  16. 通过RViz中的InteractiveMarkers在ROS中仿真力和力矩(wrench.force和wrench.torque)
  17. arduino开发板包默认安装在哪里?安装目录分析
  18. SSD: Single Shot MultiBox Detector 论文翻译
  19. 浅谈cocos2d游戏中天气系统的简单实现
  20. MAC 配置php多版本 遇到的问题

热门文章

  1. Python Excel 数组 for循环无法一次性全部删除问题原因解决
  2. 商城-下单-订单系统接口
  3. 多传感器融合之雷达图像数据集自动生成 - 20220613
  4. Redis压力测试报告
  5. Power Apps 免费社区版
  6. [资料] [转载] 图形加速卡技术 [专业的基础技术文章]
  7. 2023彩虹易支付最新原版开源网站源码
  8. 公司常见管理系统介绍(非原创)
  9. 从头学Java之equals和==的故事
  10. char*cstr 转 ushort