Spring 复习

3.循环依赖

3.1 定义

循环依赖指多个对象的创建过程中均需要注入对方对象,如下所示

class A{
B b;
public A(){
}
public A(B b){
this.b = b;
}
public void setB(B b){
this.b = b;
}
}
class B{
A a;
public B(){
}
public B(A a){
this.a = a;
}
public void setA(A a){
this.a = a;
}
}

3.2 解决

Spring中将对象创建分为如下两步

  • 实例化:创建初始对象
  • 初始化:注入属性

并且引入三级缓存,来提前暴露对象引用,从而解决循环依赖的问题

3.3 示例

假设A和B的创建中,field均需要对方的引用,在refresh方法进行到finishBeanFactoryInitialization(beanFactory)时,会开始创建非懒加载的singleton,这里会先进入preInstantiateSingletons方法,根据beanName调用getBean方法,假设此时A先进行创建,那么会进入下面方法

  • doGetBeanorg.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

    • getSingleton---1

      @Override
      @Nullable
      public Object getSingleton(String beanName) {
      return getSingleton(beanName, true);
      } @Nullable
      protected Object getSingleton(String beanName, boolean allowEarlyReference) {
      Object singletonObject = this.singletonObjects.get(beanName);
      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;
      }

      首先调用上面方法先从singletonObjects中场是获取,发现为null,由于isSingletonCurrentlyInCreation为false(对象未在创建过程中),因此直接返回null

      if (mbd.isSingleton()) {
      sharedInstance = getSingleton(beanName, () -> {
      try {
      return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
      // Explicitly remove instance from singleton cache: It might have been put there
      // eagerly by the creation process, to allow for circular reference resolution.
      // Also remove any beans that received a temporary reference to the bean.
      destroySingleton(beanName);
      throw ex;
      }
      });
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }

      其中执行DefaultSingletonBeanRegistry#getSingleton(beanName,ObjectFactory)方法简化版如下,传入的ObjectFactory实现类是一个lambda表达式,也即用createBean方法重写ObjectFactory#getObject方法

      • getSingleton---2
      public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
      Assert.notNull(beanName, "Bean name must not be null");
      synchronized (this.singletonObjects) {
      boolean newSingleton = false; try {
      singletonObject = singletonFactory.getObject();
      newSingleton = true;
      }
      catch (IllegalStateException ex) {
      }
      }
      catch (BeanCreationException ex) {
      }
      finally { }
      if (newSingleton) {
      addSingleton(beanName, singletonObject);
      }
      }
      return singletonObject;
      }
      }

      这里第一行调用singletonFactory.getObject方法会触发createBean,又触发AbstractAutowireCapableBeanFactory#doCreateBean方法中主题步骤如下

      • 实例化bean

      • 将bean放入三级缓存singletonFactories

        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
        "' to allow for resolving potential circular references");
        }
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        其中调用addSingletonFactory方法如下,此处传入的lambda表达式给定的即为ObjectFactory对象,在执行其getObject方法时,即执行getEarlyBeanReference方法(这里需要留意!)

        protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
        this.singletonFactories.put(beanName, singletonFactory);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
        }
        }
        }
      • A执行populateBean,开始注入属性b,由于B的对象还未创建,getSingleton---1(b)为null,这时触发B对象创建

      • B进行实例化

      • B放入三级缓存

      • B执行populateBean,开始注入属性a,调用getSingleton---1方法获取a,发现一级缓存singletonObject中没有对应对象,且正在创建中,则从二级缓存earlySingletonObjects中获取,发现仍然为null且allowEarlyReference默认为true,则去三级缓存中去获取,最终从三级缓存中获取,由于放入三级缓存时,lambda表达式为() -> getEarlyBeanReference(beanName, mbd, bean),所以会调用getEarlyBeanReference方法如下

        protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
        exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
        }
        }
        }
        return exposedObject;
        }

        这里在遍历后置处理器的过程中,会调用到AbstractAutoProxyCreator的postProcessAfterInitialization方法,此方法会判断A是否被代理,如果被代理会创建代理对象并返回,之后将原有A对象从三级缓存中删除,并将A的代理对象加入到二级缓存earlySingletonObjects中,之后将A的代理对象注入给B

      • B执行initializeBean方法,调用后置处理器及afterProperties方法,这里提到后置处理器,同样会判断B是否被代理,如果被代理则会创建B的代理对象并返回

      • B创建结束之后,会回到getSingleton---2方法,调用addSingleton(beanName, singletonObject);方法,如下

        protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
        }
        }

        这里会将B从三级缓存中删除,并加入到一级缓存中

      • A执行initializeBean方法,进行初始化,初始化完成

      • 回到getSingleton--2,执行DefaultSingletonBeanRegistry#addSingleton

        protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
        }
        }

        将A从二级缓存中删除,并加入到一级缓存中

从上面步骤可以看出,三级缓存分别用于存放下面三类对象

  • 一级缓存singletonObjects

    完全创建好的对象,如果被代理,则存放代理对象

  • 二级缓存earlySingletonObjects

    未完全创建好的代理对象

  • 三级缓存singletonFactories

    只进行了实例化,未进行属性注入和初始化的对象

3.4 AoP的考虑

如上在有循环依赖的情况下,假设A被代理,那么需要将A的代理对象注入给B,这时通过getSingleton方法从三级缓存获取对象的过程中,由于ObjectFactory的getObject方法被重写为AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法,这时会触发后置处理器的执行,会调用AbstractAutoProxyCreator的postProcessAfterInitialization方法,并返回代理对象,之后将代理对象返回用于注入,并放入二级缓存,如果A和除了B的其他对象也构成循环依赖,之后直接从二级缓存中获取A的代理对象即可

在没有循环依赖的情况下,不会使用到二级缓存,如果A被代理,那么A会在完全创建后,在调用后置处理器序列时,会调用AbstractAutoProxyCreator的postProcessAfterInitialization方法,并返回代理对象

从上可以看出,

  • Spring的机制是尽量让代理对象靠后创建,也即在没有循环依赖时在对象完全创建后再创建代理对象
  • 在延迟创建代理对象的机制下,必须有二级缓存,这样在从三级缓存中获取时,会调用ObjectFactory方法,其又调用getEarlyBeanReference方法完成代理对象创建,之后二级缓存用于存储代理对象,而一级缓存用于存放完全创建完成的对象
  • Spring中,如果调用某个代理对象a的方法,其中又调用了代理对象b的方法,而不是对象b的方法

# 参考

Spring循环依赖三级缓存是否可以减少为二级缓存? - SegmentFault 思否

高频面试题:Spring 如何解决循环依赖? - 知乎 (zhihu.com)

Spring-bean的循环依赖以及解决方式_惜暮-CSDN博客_spring 循环依赖

Spring IoC - 循环依赖的更多相关文章

  1. Spring IoC 循环依赖的处理

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 是 ...

  2. 曹工说Spring Boot源码(29)-- Spring 解决循环依赖为什么使用三级缓存,而不是二级缓存

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  3. Spring的循环依赖问题

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

  4. Spring之循环依赖

    转:http://my.oschina.net/tryUcatchUfinallyU/blog/287936 概述 如何检测循环依赖 循环依赖如何解决 Spring如何解决循环依赖 主要的几个缓存 主 ...

  5. 再谈spring的循环依赖是怎么造成的?

    老生常谈,循环依赖!顾名思义嘛,就是你依赖我,我依赖你,然后就造成了循环依赖了!由于A中注入B,B中注入A导致的吗? 看起来没毛病,然而,却没有说清楚问题!甚至会让你觉得你是不清楚spring的循环依 ...

  6. Spring解决循环依赖

    1.Spring解决循环依赖 什么是循环依赖:比如A引用B,B引用C,C引用A,它们最终形成一个依赖环. 循环依赖有两种 1.构造器循环依赖 构造器注入导致的循环依赖,Spring是无法解决的,只能抛 ...

  7. Spring当中循环依赖很少有人讲,今天一起来学习!

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...

  8. Spring的循环依赖,学就完事了【附源码】

    目录 啥是循环依赖? Spring可以解决循环依赖的条件 Spring如何去解决循环依赖 SpringBean的创建流程 Spring维护的三级缓存 getSingleton getSingleton ...

  9. Spring的循环依赖

    本文简要介绍了循环依赖以及Spring解决循环依赖的过程 一.定义 循环依赖是指对象之间的循环依赖,即2个或以上的对象互相持有对方,最终形成闭环.这里的对象特指单例对象. 二.表现形式 对象之间的循环 ...

随机推荐

  1. Git轻松入门2:分支篇

    什么是分支 在玩剧情类游戏时,不同的选择会触发不同的剧情路线,每条剧情路线都会独立发展,最终走向不同的结局. Git中所谓的"分支(branch)"就如同游戏中的剧情路线,用户可以 ...

  2. redis分布式锁的这些坑,我怀疑你是假的开发

    摘要:用锁遇到过哪些问题? 一.白话分布式 什么是分布式,用最简单的话来说,就是为了较低单个服务器的压力,将功能分布在不同的机器上面:就比如: 本来一个程序员可以完成一个项目:需求->设计-&g ...

  3. ACM-ICPC 2018 南京赛区网络预赛(12/12)

    ACM-ICPC 2018 南京赛区网络预赛 A. An Olympian Math Problem 计算\(\sum_{i=1}^{n-1}i\cdot i!(MOD\ n)\) \(\sum_{i ...

  4. 矩阵树定理(Kirchhoff || Laplace)初探——Part 1(无向图计数)

    必备知识: 高斯消元,图论基本知识(好像就这...(雾)) 这里是无向图部分,请不要走错场... 定义 我们将邻接矩阵定义为矩阵A(u,v),我想邻接矩阵就不用再多说了: 我们将每个点的度数矩阵定义为 ...

  5. kafka消息队列、环境搭建与使用(.net framework)

    一:kafka介绍 kafka(官网地址:http://kafka.apache.org)是一种高吞吐量的分布式发布订阅的消息队列系统,具有高性能和高吞吐率. 1.1 术语介绍 BrokerKafka ...

  6. leetcode_二叉树篇_python

    主要是深度遍历和层序遍历的递归和迭代写法. 另外注意:因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中). 所有题目首先考虑root否是空.有的 ...

  7. Win10永久禁用驱动程序强制签名

    在win10下用一个命令就可以禁用驱动程序强制签名 1.禁止强制签名,以管理员的身份运行cmd 执行以下命令 bcdedit.exe /set nointegritychecks on 恢复默认验证, ...

  8. bzoj4695 最假女选手(势能线段树/吉司机线段树)题解

    题意: 已知\(n\)个数字,进行以下操作: \(1.\)给一个区间\([L,R]\) 加上一个数\(x\) \(2.\)把一个区间\([L,R]\) 里小于\(x\) 的数变成\(x\) \(3.\ ...

  9. QUIC协议文档翻译——什么是QUIC

    原文地址https://docs.google.com/document/d/1gY9-YNDNAB1eip-RTPbqphgySwSNSDHLq9D5Bty4FSU/edit QUIC是一个谷歌提出 ...

  10. Eclipce怎么恢复误删类

    选择误删除文件在eclipse所在包(文件夹) 在包上单击右键. 选择restore from local history... 在弹出的对话框中选择需要恢复的文件