1. 什么是循环依赖

循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B,B依赖于C,C⼜依赖于A

注意:

这⾥不是函数的循环调⽤,是对象的相互依赖关系。

循环调⽤其实就是⼀个死循环,除⾮有终结 条件。

Spring中循环依赖场景有:

  • 构造器的循环依赖(构造器注⼊)
  • Field 属性的循环依赖(set注⼊)

其中,构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。


2. 循环依赖处理机制

  • 单例 bean 构造器参数循环依赖(⽆法解决)
  • prototype 原型 bean循环依赖(⽆法解决)

    因为prototype 原型 bean ,产生对象之后是不在容器中管理的。
  • 单例bean通过setXxx或者@Autowired进行循环依赖(可以解决)

2.1 演示场景:

//lagouBen 依赖于 ItBean
public class LagouBean { private ItBean itBean; public void setItBean(ItBean itBean) {
this.itBean = itBean;
} public LagouBean() {
System.out.println("LagouBean 构造器");
}
}
//ItBean 依赖于 LagouBen
public class ItBean {
private LagouBean lagouBean; public void setLagouBean(LagouBean lagouBean) {
this.lagouBean = lagouBean;
} public ItBean() {
System.out.println("ItBean...构造器");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="lagouBean" class="com.lagou.edu.LagouBean" >
<property name="ItBean" ref="itBean>"></property>
</bean> <bean id="itBean" class="com.lagou.edu.ItBean" >
<property name="LagouBean" ref="lagouBean>"></property>
</bean> </beans>

2.2 处理机制简图

总结:

A依赖于B ,B 依赖于A

A在创建过程中 :

  1. 首先会创建Bean实例(仅仅调用构造方法,但是尚未设置属性,通过反射完成对象的初始化),

  2. 然后判断是否是单例,是否有循环依赖。

  3. 把创建好的Bean实例放入三级缓存——singletonFactories

  4. 然后将要给A Bean装配属性,发现依赖B

  5. 调用deGetBean() 想拿到B,首先从一级缓存singletonObjects)中拿,然后从二级缓存中earlySingletonObjects)拿,然后从三级缓存singletonFactories)拿,都拿不到,那就开始创建B Bean

  6. 把创建好的B Bean实例放入三级缓存——singletonFactories,发现依赖于A

  7. 调用deGetBean() 想拿到A,首先从一级缓存singletonObjects)中拿,然后从二级缓存中earlySingletonObjects)拿,都没拿到。然后从三级缓存中拿,拿到了

  8. 拿到A Bean之后如上图,放到二级缓存(earlySingletonObjects)中,然后从三级缓存(singletonFactories)中删除。然后给B bean赋值了。

  9. 此时B Bean 就装配好了 放入一级缓存池中。

  10. B 装配好了之后,A 就能顺利的装配了,然后调用addSingleton()方法,把A 从二级三级缓存中删除,然后放到一级缓存也就是单例池中

  11. 完成

注意:

这个案例中,B不会放到二级缓存,只有在B依赖的一个对象尚未实例化的时候才会把B放到二级缓存。例如:

A依赖B,B依赖A和C,C依赖B。 先创建A,把尚未赋值的A放到三级缓存,然后赋值B,找不到B,然后创建B,然后把尚未赋值的B放到三级缓存,然后在创建B的过程中从三级缓存找A(同时把A从三级缓存中删除然后加入到二级缓存),然后B还有个属性C,赋值C,从缓存中找不到C,然后创建C,然后把尚未赋值的B放到三级缓存,创建C的过程中发现C依赖于B,然后可以从三级缓存中找到B,然后把B放到二级缓存,C就装配完毕了,放到一级缓存。同时B也有了A和C,B装配完毕了,放到一级缓存。 A依赖B,B已经OK了,那么A也装配完毕了。

/** Cache of singleton factories: bean name to ObjectFactory. */
//三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */
//二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); /** Cache of singleton objects: bean name to bean instance. */
//一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

SpringIOC循环依赖的更多相关文章

  1. kmdjs和循环依赖

    循环依赖 循环依赖是非常必要的,有的程序写着写着就循环依赖了,可以提取出一个对象来共同依赖解决循环依赖,但是有时会破坏程序的逻辑自封闭和高内聚.所以没解决好循环依赖的模块化库.框架.编译器都不是一个好 ...

  2. spring3 循环依赖

    循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个环.此处不 ...

  3. 在.NET Core中遭遇循环依赖问题"A circular dependency was detected"

    今天在将一个项目迁移至ASP.NET Core的过程中遭遇一个循环依赖问题,错误信息如下: A circular dependency was detected for the service of ...

  4. seaJS循环依赖的解决原理

    seajs模块的六个状态. var STATUS = {  'FETCHING': 1, // The module file is fetching now. 模块正在下载中  'FETCHED': ...

  5. RequireJS 循环依赖报 模块undefined 处理方案

    RequireJS 循环依赖 开始学习使用RequireJS之后做了几个小例子,之后想着把手头的项目也用RequireJS写一遍试试.感觉胜利就在前方了,忽然发现始终卡在一个问题上: 很常见的一个问题 ...

  6. 剑指架构师系列-Struts2构造函数的循环依赖注入

    Struts2可以完成构造函数的循环依赖注入,来看看Struts2的大师们是怎么做到的吧! 首先定义IBlood与BloodImpl类: public interface IBlood { } pub ...

  7. Spring的循环依赖问题

    spring容器循环依赖包括构造器循环依赖和setter循环依赖,那Spring容器如何解决循环依赖呢?首先让我们来定义循环引用类: 在Spring中将循环依赖的处理分成了3种情况: 构造器循环依赖 ...

  8. Spring对加载的bean之间循环依赖的处理

    根据下面文档的叙述,简言之: 对于相互之间通过构造函数注入相互循环依赖的情况,Spring会抛出BeanCurrentlyInCreationException错误. 如果AB两个beans是通过属性 ...

  9. DI 之 3.2 循环依赖 (伍)

    3.2.1  什么是循环依赖 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA, ...

随机推荐

  1. Java学习的第十四天

    1.JAVA的继承使用关键字extends继承 class 子类 extends 父类{} 子类可以用super来调用父类的非私有属性和非私有方法,还可以调用非私有的父类构造函数 如果父类子类的方法名 ...

  2. php中Standard中配置选项,在TargetFrameworks环境下如何输出库存

    在.NET Standard/.NET Core技术出现之前,编写一个类库项目(暂且称为基础通用类库PA)且需要支持不同 .NET Framework 版本,那么可行的办法就是创建多个不同版本的项目( ...

  3. swjtuoj2433 Magic Mirror

    描述 Magic Mirror is an artificial intelligence system developed by TAL AI LAB,It can determine human ...

  4. C#编译时与运行时

    曾几何时,对C#编译时与运行时的理解总是不是那么明显.以下对此部分说明一下自己的理解. 定义 编译时 将C#程序编译成中间代码的过程.其过程是对程序进行词法分析,语法分析等. 运行时 就是程序最终分配 ...

  5. 对于button元素的理解

    button有四种常用的类型: submit:  此按钮将表单数据提交给服务器.如果未指定属性,或者属性动态更改为空值或无效值,则此值为默认值. reset:  此按钮重置所有组件为初始值. butt ...

  6. python爬虫07BeautifulSoup

    有一个高效的网页解析库 它的名字叫做 BeautifulSoup 它   是一个可以从 HTML 或 XML 文件中提取数据的 Python 库 首先我们要安装一下这个库 pip install be ...

  7. 企业中真实需要的集中管理软件SVN即Subversion版本控制

    一.SVN基本概念 SVN是Subversion的简称,是一个自由开源的版本控制系统. checkout: 把整个项目源码下载到本地 update: 从服务器上更新代码,使本地达到最新版本 commi ...

  8. layuiu按钮

    1.关于layui图标 唯一要提的是这是一个矢量图标 因此可以像对待文字一样加上style = font-size  以及color属性 eg: <i class="layui-ico ...

  9. 记一次ns3的安装过程

    官方安装教程:https://www.nsnam.org/wiki/Installation 推荐使用Ubuntu18.04,Ubuntu20.04有些依赖无法下载. 准备工作 # 如果下载速度很慢, ...

  10. fcntl函数用法——操纵文件描述符状态

    fcntl函数:操纵文件描述符,改变已经打开的文件的属性int fcntl(int fd, int cmd, ... //arg  );cmd选项:一.复制文件描述符:F_DUPFD二.更改设置文件描 ...