SpringIOC DI循环依赖实例详解

要弄清楚循环依赖

1、需要知道Bean初始化的两个阶段

① Bean实例化创建实例对象(new Bean())

② Bean实例对象初始化(DI:注解自动注入)

2、DefaultSingletonBeanRegistry类中的5个容器

/** 记录已将创建的单例<beanName,singletonBean> */
  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

  /** 记录singletonFactory<beanName,singletonFactory> singeletonFactory中存放beanName和上面的①阶段的bean:Bean实例化实例对象(还未初始化DI)*/
  private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

  /** 记录早期的singletonBean 存放的也是① */
  private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

  /** 存放已经初始化后的beanName,有序 */
  private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

  /** 记录正在初始化的bean的beanName */
  private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

其中跟循环依赖相关的是singletonFactories、singeletonsCurrentlyInCreation、earlysingletonObjects.

3、循环依赖实现

①bean初始化前后会打标,加入到singletonsCurrentlyInCreation容器中,这个打标会在核心方法getSingleton()中起作用

/* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>) */
  public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    ...
    //循环依赖相关:初始化前先singletonsCurrentlyInCreation.add(beanName)
    beforeSingletonCreation(beanName);
    ...
    //lamda表达式:其实是调用createBean(beanName, mbd, args):Bean初始化
    singletonObject = singletonFactory.getObject();
    ...
    //循环依赖相关:初始化后singletonsCurrentlyInCreation.remove(beanName)
    afterSingletonCreation(beanName);
    ...//初始化完后
    //this.singletonObjects.put(beanName, singletonObject);放入到单例容器中
    //this.singletonFactories.remove(beanName);清空循环依赖的两个打标
    //this.earlySingletonObjects.remove(beanName);
    //this.registeredSingletons.add(beanName);放入单例beanName容器中
    addSingleton(beanName, singletonObject);
    ...
    }
  }

② 上面singletonObject = singletonFactory.getObject()时向singletonFactories中记录了(new Bean()),singletonFactories也会在核心方法getSingleton()中起作用

/* org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean */
  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
    ...
    //循环依赖相关逻辑:
    //this.singletonFactories.put(beanName, singletonFactory);
    //将实例化bean(①阶段)、beanName组装成singletonFactory装入singletonFactories容器
    //this.earlySingletonObjects.remove(beanName);
    //删除earlySingletonObjects中beanName
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    ...
    //实例初始化 就是在这里面实现依赖注入DI的:反射实现
    //调用AutowiredAnnotationBeanPostProcessor.postProcessProperties
    populateBean(beanName, mbd, instanceWrapper);
    ...
  }

③ 核心方法getSingleton

/* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) */
  protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
     //循环依赖核心就在于这个判断,由于打标+记录了①阶段的bean,
    //循环依赖第二次调用getBean("a")时,这里会直接返回第一次调用getBean("a")创建的①阶段的bean
    //而不会调用createBean("a")再次bean初始化(造成两个bean的循环创建)
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
          if (singletonFactory != null) {
            singletonObject = singletonFactory.getObject();
            this.earlySingletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
          }
        }
      }
    }
    return singletonObject;
  }

④ 循环依赖流程

/* org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean */
  protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    ...
    //假设A、B互相依赖
    //第一次getBean(A),sharedInstance == null,走else,createBean
    //A正在创建打标,①中beforeSingletonCreation()
    //A实例化后保存到singletonFactories中②中addSingletonFactory(beanName,singletonFactory)
    //DI依赖注入:②中populateBean(beanName,mbd,instanceWrapper),发现依赖B,调用getBean(B)初始化B的单例
    //调用getBean(B)重复上面步骤,DI依赖注入发现依赖A,调用getBean(A)
    //第二次getBean(A),③中if(singletonObject == null && isSingletonCurrentlyInCreation(A))由于打标了所以返回singleFactory.getObject()
    //下面if条件直接返回bean,没有走else破坏了循环
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
      //
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
      ...
      //
      createBean(beanName, mbd, args);
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    return bean;
  }

四、总结

未看源码之前,其实对循环依赖有一个想法:循环依赖可以看做是一个死锁。

预防死锁的方法:打破死锁的四个必要条件(互斥、请求并等待、不可剥夺、循环等待),由于循环依赖的资源是对象自身,所以常用破坏循环等待条件方法:编号顺序执行,不适用

选择破坏请求并等待条件:先创建对象,再赋值,模型

A a = new A();
B b = new B();
A.b = b;
B.a = a;

研究源码之后发现:想法差不多,但是代码实现非常精彩。模型(打标没想到过)

A a = new A();
B b = new B();
b.a = a;
a.b = b;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring创建IOC容器的方式解析

    1.直接得到 IOC 容器对象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); 封装起来: public class ApplicationContextUtil { private static ApplicationContext applicationContext = null; public ApplicationCont

  • Spring核心容器IOC原理实例解析

    这篇文章主要介绍了Spring核心容器IOC原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一 .BeanFactory Spring Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,也即 IOC 容器为开发者管理对象 间的依赖关系提供了很多便利和基础服务.最基本的 IOC 容器接口 BeanFactory,来看一下它的源码: public interface BeanFactory { //对 FactoryBean

  • SPRING IOC注入方式过程解析

    这篇文章主要介绍了SPRING IOC注入方式过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring IOC注入的方法主要有两种 1:设值注入 2:构造注入 简单来说一个是调用set方法设值,一个是通过构造函数设值 Spring-ioc.xml <?xml version= "1.0" encoding= "UTF-8"?> <beans xmlns= "http://ww

  • Spring为IOC容器注入Bean的五种方式详解

    这篇文章主要介绍了Spring为IOC容器注入Bean的五种方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一 @Import导入组件,id默认是组件的全类名 //类中组件统一设置.满足当前条件,这个类中配置的所有bean注册才能生效: @Conditional({WindowsCondition.class}) @Configuration @Import({Color.class,Red.class,MyImportSelector

  • 创建Maven项目和Spring IOC实例过程解析

    这篇文章主要介绍了创建Maven项目和Spring IOC实例过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 把如何创建Maven项目和创建Spring IOC的例子分享给大家,希望能对大家有帮助! 一.创建Maven项目 我用的是Intellij IDEA开发工具创建Maven项目的,打开该软件后,直接点击file --->project,如下图所示, 然后就直接跟着我的图片的步骤往下走. 到了这一个就创建好了Maven项目了,然后开

  • 简单了解SPRINGIOC的底层原理演变过程

    1.传统方式 UserService us = new UserService(); (UserService为一个java类,直接实例化成对象再进行操作) 2.面向接口 UserService us = new UserServiceImp(); (UserService为一个接口,UserServiceImp为接口实现类) 这样会导致web层和业务层产生耦合,程序设计应满足ocp原则 此时,若我想切换实现类,则我需要在代码中将UserServiceImp修改掉 3.工厂模式 创建工厂类,通过

  • Spring IOC和aop的原理及实例详解

    这篇文章主要介绍了Spring IOC和aop的原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架.特点是面向接口编程,松耦合. 1:IOC(控制反转) 别名(DI:依赖注入) 首先来一段ioc的实现原来代码: public class ClassPathXmlApplicationContext implements BeanFactory { privat

  • SpringIOC DI循环依赖实例详解

    要弄清楚循环依赖 1.需要知道Bean初始化的两个阶段 ① Bean实例化创建实例对象(new Bean()) ② Bean实例对象初始化(DI:注解自动注入) 2.DefaultSingletonBeanRegistry类中的5个容器 /** 记录已将创建的单例<beanName,singletonBean> */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(

  • Linux shell数组循环的实例详解

    shell数组循环 测试shell数组,循环的例子: arr=("a" "b" "c") echo "所有的内容如下:"${arr[@]} echo "数组的长度:"${#arr[*]} for var in ${arr[@]} do echo "打印的内容:"$var done 输出的内容如下: 以上就是Linux shell数组循环的实例详解,如有疑问请留言或者到本站社区交流讨论,感

  • 对python while循环和双重循环的实例详解

    废话不多说,直接上代码吧! #python中,while语句用于循环执行程序,即在某个条件下,循环执行某段程序,以处理需要重复处理的相同任务. #while是"当型"循环结构. i=1 while i<=20: print(i,end=" ") i+=1 sum=0 i=1 while i<=100: sum+=i i+=1 else: print("\n",sum) print("0+2+...+100=",su

  • spring如何解决循环依赖问题详解

    循环依赖其实就是循环引用,很多地方都说需要两个或则两个以上的bean互相持有对方最终形成闭环才是循环依赖,比如A依赖于B,B依赖于C,C又依赖于A.其实一个bean持有自己类型的属性也会产生循环依赖. setter singleton循环依赖 使用 SingleSetterBeanA依赖SingleSetterBeanB,SingleSetterBeanB依赖SingleSetterBeanA. @Data public class SingleSetterBeanA { @Autowired

  • JavaScript数据结构之优先队列与循环队列实例详解

    本文实例讲述了JavaScript数据结构之优先队列与循环队列.分享给大家供大家参考,具体如下: 优先队列 实现一个优先队列:设置优先级,然后在正确的位置添加元素. 我们这里实现的是最小优先队列,优先级的值小(优先级高)的元素被放置在队列前面. //创建一个类来表示优先队列 function Priorityqueue(){ var items=[];//保存队列里的元素 function QueueEle(e,p){//元素节点,有两个属性 this.element=e;//值 this.pr

  • Python中分支语句与循环语句实例详解

    前言 本篇博文介绍一下Python中的if条件语句.while循环语句.for in循环语句以及break和continue控制关键字. 分支的基本语法 if 条件表达式: 语句1 语句2 语句3 ...... 条件表达式就是计算结果必须为布尔值的表达式 表达式后面的冒号不能少 注意if后面的出现的语句,如果属于if语句块,则必须同一个锁紧等 if条件控制语句 条件控制语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块.if后面应该接一个条件,即一个布尔类型.而且Py

  • 微信小程序wx:for循环的实例详解

    列表渲染 wx:for 在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件. 默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item. <view wx:for="{{array}}"> {{index}}: {{item.message}} </view> Page({ data: { array: [{ message: 'foo', }, { message: 'bar' }] } }) 使

  • Python中最快的循环姿势实例详解

    目录 各种姿势 比较快的姿势 最后 各种姿势 比如说有一个简单的任务,就是从 1 累加到 1 亿,我们至少可以有 7 种方法来实现,列举如下: 1.while 循环 def while_loop(n=100_000_000): i = 0 s = 0 while i < n: s += i i += 1 return s 2.for 循环 def for_loop(n=100_000_000): s = 0 for i in range(n): s += i return s 3.sum ran

  • Go依赖注入DI工具wire使用详解(golang常用库包)

    目录 什么是依赖注入 第一次编写mysql操作类: 第二次编写mysql操作类: 第三次编写mysql操作类: 何时使用依赖注入 wire 概念说明 provider 和 injector provider injector wire 使用 快速开始 小结 绑定接口 Provider Set 参考 google 出品的依赖注入库 wire:https://github.com/google/wire 什么是依赖注入 依赖注入 ,英文全名是 dependency injection,简写为 DI.

  • .net程序开发IOC控制反转和DI依赖注入详解

    目录 IOC控制反转 DI依赖注入 服务生命周期 其它 IOC控制反转 大部分应用程序都是这样编写的:编译时依赖关系顺着运行时执行的方向流动,从而生成一个直接依赖项关系图. 也就是说,如果类 A 调用类 B 的方法,类 B 调用 C 类的方法,则在编译时,类 A 将取决于类 B,而 B 类又取决于类 C 应用程序中的依赖关系方向应该是抽象的方向,而不是实现详细信息的方向.而这就是控制反转的思想. 应用依赖关系反转原则后,A 可以调用 B 实现的抽象上的方法,让 A 可以在运行时调用 B,而 B

随机推荐