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. OpenCascade拓扑对象之:拓扑对象的层次结构

    @font-face { font-family: "Times New Roman" } @font-face { font-family: "宋体" } @ ...

  2. P2868 [USACO07DEC]Sightseeing Cows G

    题意描述 Sightseeing Cows G 给定一张有向图,图中每个点都有点权 \(a_i\),每条边都有边权 \(e_i\). 求图中一个环,使 "环上个点权之和" 除以 & ...

  3. 2018-12-5 及 codeforces round 525v2

    突然发现五天没记录了,这五天学习完全没有按着正常规划进行,先罗列一下吧. 机器学习视频第一周的全部看完了. 算法导论看了几页. 参加了一次CF.rating只加了20,看来提高实力才是最关键的. C+ ...

  4. 【Android Studio】安卓开发初体验2——Activity

    Activity是什么 Activity用于提供可视化用户界面的组件,可以与用户进行交互来完成某项任务,一个应用程序中可以包含零个或多个活动 Activity的创建 首先将左侧的Active Tool ...

  5. MapReduce在Shuffle阶段按Mapper输出的Value进行排序

    ZKe ----------------- 在MapReduce框架中,Mapper的输出在Shuffle阶段,根据Key值分组之后,还将会根据Key值进行排序,因此Reducer的输出我们看到的结果 ...

  6. mysql存储过程加事务

    create procedure sp_sw2() begin declare error int default 0; declare continue handler for SQLEXCEPTI ...

  7. EVM 2.0 预览版,跃见非凡,源于鸿蒙,开启物联网小程序新时代

    1. 基于EVUE的物联网小程序 EVM诞生以来,一直致力于让物联网开发变得简单,自鸿蒙OS 1.0 发布后,最引人注目的莫过于华为在应用程序开发框架层面面向应用开发者提供了一种全新的开发方式: 框架 ...

  8. svn“Previous operation has not finished; run 'cleanup' if it was interrupted“报错的解决方案

    今天SVN提交代码遇到"Previous operation has not finished; run 'cleanup' if it was interrupted"报错,&q ...

  9. tcp输入数据 慢速路径处理 && oob数据 接收 && 数据接收 tcp_data_queue

    大致的处理过程 TCP的接收流程:在tcp_v4_do_rcv中的相关处理(网卡收到报文触发)中,会首先通过tcp_check_urg设置tcp_sock的urg_data为TCP_URG_NOTYE ...

  10. 329. Longest Increasing Path in a Matrix(核心在于缓存遍历过程中的中间结果)

    Given an integer matrix, find the length of the longest increasing path. From each cell, you can eit ...