一、什么是循环依赖

循环依赖即循环引用,形成闭环。比如,A 依赖 B,B 又依赖 A,形成了循环依赖;或者 A 依赖 B,B 依赖 C,C 又依赖 A,形成了循环依赖;更或者是自己依赖自己。如图:

这不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。Spring 循环依赖场景有:

  1. 构造器的循环依赖
  2. 构造器依赖 + field/setter 依赖(A 的构造器依赖 B,B 的 field/setter 依赖 A)
  3. field 依赖或者 setter 依赖

具体情况如下:
1️⃣A 的构造方法中依赖 B 的实例对象,同时 B 的构造方法中依赖 A 的实例对象。(无法解决)
2️⃣A 的构造方法中依赖 B 的实例对象,同时 B 的某个 field/setter 需要 A 的实例对象,以及反之。(可以解决)
3️⃣A 的某个 field/setter 依赖 B 的实例对象,同时 B 的某个 field/setter 依赖 A 的实例对象,以及反之。(可以解决)

二、Spring 三大循环依赖

1️⃣构造器注入循环依赖【不能解决】

表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException表示循环依赖。

@Service
public class A {public A(B b) {}
}
@Service
public class B {public B(A a) {}
}

Spring 容器会将每一个正在创建的 Bean 标识符放在一个“当前创建 Bean 池”中,Bean 标识符在创建过程中将一直保持在这个池中,因此如果在创建 Bean 过程中发现自己已经在“当前创建 Bean 池”里时将抛出BeanCurrentlyInCreationException表示循环依赖;而对于创建完毕的 Bean 将从“当前创建 Bean 池”中清除掉。执行结果报错信息为:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:  Error creating bean with name 'a': Requested bean is currently in creation:
Is there an unresolvable circular reference?

根本原因:Spring 解决循环依赖依靠的是 Bean 的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态。而构造器表示已经完成实例化,所以构造器的循环依赖无法解决。

2️⃣Singleton Pattern field 属性注入循环依赖【可以解决】

@Service
public class A {@Autowiredprivate B b;
}@Service
public class B {@Autowiredprivate A a;
}

结果:项目启动成功,能够正常工作。

备注:setter 方法注入方式原理同字段注入方式类似。

3️⃣prototype 模式 field 属性注入循环依赖【不能解决】

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class A {@Autowiredprivate B b;
}@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class B {@Autowiredprivate A a;
}

对于prototype 作用域的 bean,Spring 容器无法完成依赖注入,因为 Spring 容器不缓存 prototype 作用域的 bean,因此无法提前暴露一个创建中的 bean。

scope="prototype"意思是每次请求都会创建一个实例对象。两者的区别是:有状态的 bean 都使用 Prototype 作用域,无状态的一般都使用 singleton 单例作用域。

因此本例中启动时是不会报错的(因为非单例 Bean 默认不会初始化,而是使用时才会初始化),所以需要手动 getBean() 或者在一个单例 Bean 内 @Autowired 一下它即可:

// 在单例Bean内注入@Autowiredprivate A a;

打印结果:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:  Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

三、怎么检测是否存在循环依赖

检测循环依赖相对比较容易,Bean 在创建的时候可以给该 Bean 打标,如果递归调用回来发现正在创建中的话,说明循环依赖了。

四、Spring 是如果解决循环依赖的

Spring 通过三级缓存加上“提前曝光”机制,配合 Java 的对象引用原理,比较完美地解决了某些情况下的循环依赖问题。

  1. A 首先完成了初始化的第一步,并且将自己提前曝光到 singletonFactories 中。此时进行初始化的第二步,发现自己依赖对象 B,就尝试去 get(B),发现 B 还没有被 create,所以走 create 流程。
  2. B 在初始化第一步的时候发现自己依赖了对象 A,于是尝试 get(A),由于 A 通过 ObjectFactory 将自己提前曝光了,所以 B 能够通过 ObjectFactory.getObject 拿到 A 对象。
  3. B 拿到 A 对象后顺利完成了初始化阶段 1、2、3,完全初始化之后将自己放入到一级缓存 singletonObjects 中。此时返回 A 中,A 拿到 B 的对象顺利完成自己的初始化阶段 2、3,最终 A 也完成了初始化。

Spring 循环依赖(circular dependency)相关推荐

  1. 【spring容器启动】之bean的实例化和初始化(文末附:spring循环依赖原理)

    本次我们通过源码介绍ApplicationContext容器初始化流程,主要介绍容器内bean的实例化和初始化过程.ApplicationContext是Spring推出的先进Ioc容器,它继承了旧版 ...

  2. Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)

    总览 问题 详解 String.intern()的作用 link LeetCode的Two Sum题 link 什么是可重入锁? link 谈谈LockSupport link 谈谈AQS link ...

  3. Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)_3

    Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)_3 总览 问题 详解 String.intern()的作用 link LeetCode的Two Sum题 ...

  4. spring循环依赖

    spring循环依赖主要有三种:         单例引用类型循环依赖(属性):允许         构造器的循环依赖:不允许         多例循环依赖:不允许 单例引用类型循环依赖(属性) pa ...

  5. 烂了大街的 Spring 循环依赖问题,你以为自己就真会了吗

    前言 循环依赖问题,算是一道烂大街的面试题了,解毒之前,我们先来回顾两个知识点: 初学 Spring 的时候,我们就知道 IOC,控制反转么,它将原本在程序中手动创建对象的控制权,交由 Spring ...

  6. 这个Spring循环依赖的坑,90%以上的人都不知道

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Mythsman 原文:https://blog.myths ...

  7. 这个 Spring 循环依赖的坑,90% 以上的人都不知道

    点击上方"后端技术精选",选择"置顶公众号" 技术文章第一时间送达! 作者:Mythsman blog.mythsman.com/post/5d838c7c2d ...

  8. Spring循环依赖的三种方式以及解决办法

    Spring循环依赖的三种方式以及解决办法 [转]https://www.cnblogs.com/liuqing576598117/p/11227007.html 示例 https://github. ...

  9. 聊透Spring循环依赖

    本文聊一下和依赖注入密切相关,并且在实际开发中很常见,面试也很喜欢问的一个问题:Spring是怎么解决循环依赖的?  之前就被问过Spring是怎么解决循环依赖的问题,当时年少无知,对Spring源码 ...

最新文章

  1. 公开课:如何成为一名高级系统运维工程师(架构师)?
  2. python 循环批量生成变量
  3. Jvm 系列(三):GC 算法 垃圾收集器
  4. 设计模式复习-原型模式
  5. 记录一次缓存系统的优化过程
  6. python常用指令速查
  7. Mybatis3 (2)xml映射文件
  8. java迭代器删除两个_两个迭代器的故事
  9. Wise Package Studio介绍
  10. How to Configure Eclipse for Python --- 在eclipse中如何配置pydev
  11. STORM启动与部署TOPOLOGY
  12. C语言实现FFT算法
  13. msvcr71.dll缺失导致Oracle无法连接
  14. 演示u盘启动盘装系统教程
  15. 【从蛋壳到满天飞】JS 数据结构解析和算法实现-堆和优先队列(二)
  16. 西游记笔记与想法(2)
  17. 移动web学习(一) --- 流式布局, 视口viewpoint ,2倍图和3倍图, less
  18. 北航计算机组成实验课,北航计算机组成实验Project4
  19. 定时器、看门狗和RTC
  20. SAP中GR IR PGI的含义

热门文章

  1. python爬虫(五)爬虫实战
  2. stratum mining protocol demo
  3. kindeditor+粘贴word图片
  4. 服务器芯片市场国产,飞腾发布国产腾云 S2500服务器芯片 2020中国服务器行业市场全景调研...
  5. 手动清除jwgkvsq.vmx病毒
  6. 外贸软件如何提升进出口公司业绩 实现降本增效
  7. 中招报名网站服务器错误,中考报名显示密码错误 中考网上报名说我密码错误怎么办...
  8. 51单片机控制LCD1602模块
  9. 计算机域名DNS设置,电脑设置和查看DNS域名服务器的技巧
  10. 计算机组装硬件要求,组装电脑必懂的硬件知识,全是干货,教你选购硬件不求人...