spring解决循环依赖

概述

循环依赖就是依赖关系形成环,比如最简单的循环依赖:A对象依赖B,B对象依赖A

属性注入与循环依赖

  1. 如果是构造器注入,如果循环依赖对象没法构建,因为还未实例化
  2. 如果是属性注入但是作用域是prototype,spring不会缓存其对象实例,也不能处理循环依赖的情况
  3. 如果是属性注入singleton的,其bean的实例化过程与属性注入过程是分开的,并且spring提供了三个map(就是大家说三级缓存)来实现。

spring属性注入处理循环依赖的方式

通过以下xml方式配置一个循环依赖的示例:

<bean id="person1" class="com.example.leetcode.spring.bean.Person">
  <property name="parent" ref="person2"></property>
  <property name="name" value="tom"></property>
</bean>

<bean id="person2" class="com.example.leetcode.spring.bean.Person">
  <property name="parent" ref="person1"></property>
  <property name="name" value="jack"></property>
</bean>

spring循环依赖处理几个关键位置:

获取bean对象

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
    @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

  final String beanName = transformedBeanName(name);
  Object bean;

  // 这里会检查单例bean是否已经在注册表,并返回。
  // Eagerly check singleton cache for manually registered singletons.
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
    if (logger.isTraceEnabled()) {
      if (isSingletonCurrentlyInCreation(beanName)) {
        logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
            "' that is not fully initialized yet - a consequence of a circular reference");
      }
      else {
        logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
      }
    }
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }
  ...
}

DefaultSingletonBeanRegistry(单例对象注册表)的几个关键属性。

  // 用来存储已经创建好的单例对象
  /** Cache of singleton objects: bean name to bean instance. */
  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

  // 用来存储单例beanname到ObjectFactory的映射
  /** 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 HashMap<>(16);

DefaultSingletonBeanRegistry.getSingleton()的实现.

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;
}

AbstractAutowireCapableBeanFactory.doCreateBean创建对象与注入属性

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
  ...
  instanceWrapper = createBeanInstance(beanName, mbd, args);
  ...
  // 检查是否提前将单例bean存入缓存
  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");
    }
    // 这里将beanname与工厂映射放入缓存注册表中(也就是上面的singletonFactories)
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  }

  ...
  // 注入依赖属性
  populateBean(beanName, mbd, instanceWrapper);
  ...

}

假设我们从beanfactory获取person1对象, 循环依赖处理流程如下:

1.通过AbstractBeanFactory.doGetBean("persion1")获取对象

2.因为一开始通过DefaultSingletonBeanRegistry.getSingleton()什么都没有,进入AbstractAutowireCapableBeanFactory.doCreateBean()进行创建

3.AutowireCapableBeanFactory.doCreateBean()里面执行完创建逻辑,因为是singleton将beanname与工厂的映射加入到addSingletonFactory()到缓存

4.开始处理person1对象的属性依赖populateBean()

5.当发现person1的parent属性是一个引用时,通过beanfactory.getBean("person2")获取依赖对象(org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference)

6.此时进入person2的创建流程, person2也没有缓存,开始实例化并加入到addSingletonFactory()到缓存

7.person2在通过populateBean()注入属性依赖发现依赖person1, 此时通过beanfactory.getBean("person1")获取依赖对象

8.此时AbstractBeanFactory.doGetBean("persion1")获取对象执行到getSingleton("person1")进行以下判断:

  • 从singletonObjects.get(beanName)获取到null
  • 进入if条件,对singletonObjects同步
  • 从earlySingletonObjects.get(beanName);获取也为null
  • 进入内层if,通过singletonFactories.get(beanName);获取到最开始bean实例化之后的beanname与工厂缓存信息
  • 获取到仅实例化完成的bean,并earlySingletonObjects.put(beanName, singletonObject);
  • 然后删除singletonFactories.remove(beanName);

9.此时从getSingleton("person1")返回了一个仅实例化尚未注入的bean引用

10.person2在第7步获取到person1仅实例化未注入的对象引用。

11.person2完成属性注入并返回。

12.person2被addSingleton(beanName, singletonObject);中singletonObjects.put(beanName, singletonObject)缓存,并删除singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);

13.person1在5步获取到person2的对象并完成属性注入

14.person1对象返回(因为一开始person2获取的是person1的引用,此时person1完成注入是能看到注入后的对象)

15.person1被addSingleton(beanName, singletonObject);中singletonObjects.put(beanName, singletonObject)缓存,并删除singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);

16.返回最终的person1对象

关于三个map(三级缓存)

在出现循环依赖时,三个map之间的流程如下:

先从singletonFactories获取工厂,并通过getObject获取对象并移除缓存,将对象缓存到earlySingletonObjects
通过earlySingletonObjects获取提前曝光的对象
对象创建并初始化完成之后,对象信息保留在singletonObjects并移除过earlySingletonObjects中的缓存

earlySingletonObjects二级缓存是鸡肋吗?

earlySingletonObjects缓存的目的是,通过三级缓存在获取对象会执行一些列的后置处理器,通过earlySingletonObjects来缓存提升性能。

以上就是spring解决循环依赖的详细内容,更多关于sping 循环依赖的资料请关注我们其它相关文章!

(0)

相关推荐

  • Spring IOC原理补充说明(循环依赖、Bean作用域等)

    前言 通过之前的几篇文章将Spring基于XML配置的IOC原理分析完成,但其中还有一些比较重要的细节没有分析总结,比如循环依赖的解决.作用域的实现原理.BeanPostProcessor的执行时机以及SpringBoot零配置实现原理(@ComponentScan.@Import.@ImportSource.@Bean注解的使用和解析)等等.下面就先来看看循环依赖是怎么解决的,在此之前一定要熟悉整个Bean的实例化过程,本篇只会贴出关键性代码. 正文 循环依赖 首先来看几个问题: 什么是循环依

  • 简单了解Spring循环依赖解决过程

    这篇文章主要介绍了简单了解Spring循环依赖解决过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚.本文就试着尽自己所能,对此做出一个较详细的解读.另,需注意一点,下文中会出现类的实例化跟类的初始化两个短语,为怕园友迷惑,事先声明一下,本文的实例化是指刚执行完构造器将一个对象new出来,但还未填充属性值的状态,而

  • 基于SpringBoot构造器注入循环依赖及解决方式

    1. 循环依赖是什么? Bean A 依赖 B,Bean B 依赖 A这种情况下出现循环依赖. Bean A → Bean B → Bean A 更复杂的间接依赖造成的循环依赖如下. Bean A → Bean B → Bean C → Bean D → Bean E → Bean A 2. 循环依赖会产生什么结果? 当Spring正在加载所有Bean时,Spring尝试以能正常创建Bean的顺序去创建Bean. 例如,有如下依赖: Bean A → Bean B → Bean C Spring

  • 浅谈Spring如何解决循环依赖的问题

    在关于Spring的面试中,我们经常会被问到一个问题,就是Spring是如何解决循环依赖的问题的.这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能够一下子思考出个中奥秘.本文主要针对这个问题,从源码的角度对其实现原理进行讲解. 1. 过程演示 关于Spring bean的创建,其本质上还是一个对象的创建,既然是对象,读者朋友一定要明白一点就是,一个完整的对象包含两部分:当前对象实例化和对象属性的实例化.在Spring中,对象的实例化是通过反射实

  • 深入理解Spring中的循环依赖

    循环依赖 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比方CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA.形成一个环状引用关系. 在使用Spring时,如果主要采用基于构造器的依赖注入方式,则可能会遇到循环依赖的情况,简而言之就是Bean A的构造器依赖于Bean B,Bean B的构造器又依赖于Bean A.在这种情况下Spring会在编译时抛出Bean

  • 详解Spring循环依赖的解决方案

    spring针对Bean之间的循环依赖,有自己的处理方案.关键点就是三级缓存.当然这种方案不能解决所有的问题,他只能解决Bean单例模式下非构造函数的循环依赖. 我们就从A->B->C-A这个初始化顺序,也就是A的Bean中需要B的实例,B的Bean中需要C的实例,C的Bean中需要A的实例,当然这种需要不是构造函数那种依赖.前提条件有了,我们就可以开始了.毫无疑问,我们会先初始化A.初始化的方法是org.springframework.beans.factory.support.Abstra

  • Spring循环依赖的解决办法,你真的懂了吗

    介绍 先说一下什么是循环依赖,循坏依赖即循环引用,两个或多个bean相互引用,最终形成一个环.Spring在初始化A的时候需要注入B,而初始化B的时候需要注入A,在Spring启动后这2个Bean都要被初始化完成 Spring的循环依赖有两种场景 构造器的循环依赖 属性的循环依赖 构造器的循环依赖,可以在构造函数中使用@Lazy注解延迟加载.在注入依赖时,先注入代理对象,当首次使用时再创建对象完成注入 属性的循环依赖主要是通过3个map来解决的 构造器的循环依赖 @Component publi

  • Spring如何解决循环依赖的问题

    前言 在面试的时候这两年有一个非常高频的关于spring的问题,那就是spring是如何解决循环依赖的.这个问题听着就是轻描淡写的一句话,其实考察的内容还是非常多的,主要还是考察的应聘者有没有研究过spring的源码.但是说实话,spring的源码其实非常复杂的,研究起来并不是个简单的事情,所以我们此篇文章只是为了解释清楚Spring是如何解决循环依赖的这个问题. 什么样的依赖算是循环依赖? 用过Spring框架的人都对依赖注入这个词不陌生,一个Java类A中存在一个属性是类B的一个对象,那么我

  • spring解决循环依赖的简单方法

    Spring内部如何解决循环依赖,一定是单默认的单例Bean中,属性互相引用的场景.比如几个Bean之间的互相引用: 或者 setter方式原型,prototype 原型(Prototype)的场景是不支持循环依赖的,因为"prototype"作用域的Bean,为每一个bean请求提供一个实例,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean,会抛出异常. 构造器参数循环依赖 Spring容器会将每一个正在创建的Bean 标识符放在一个"当前创建Bean池&q

  • spring解决循环依赖

    概述 循环依赖就是依赖关系形成环,比如最简单的循环依赖:A对象依赖B,B对象依赖A 属性注入与循环依赖 如果是构造器注入,如果循环依赖对象没法构建,因为还未实例化 如果是属性注入但是作用域是prototype,spring不会缓存其对象实例,也不能处理循环依赖的情况 如果是属性注入singleton的,其bean的实例化过程与属性注入过程是分开的,并且spring提供了三个map(就是大家说三级缓存)来实现. spring属性注入处理循环依赖的方式 通过以下xml方式配置一个循环依赖的示例: <

  • 浅谈Spring 解决循环依赖必须要三级缓存吗

    我们都知道 Spring 是通过三级缓存来解决循环依赖的,但是解决循环依赖真的需要使用到三级缓冲吗?只使用两级缓存是否可以呢?本篇文章就 Spring 是如何使用三级缓存解决循环依赖作为引子,验证两级缓存是否可以解决循环依赖. 循环依赖 既然要解决循环依赖,那么就要知道循环依赖是什么.如下图所示: 通过上图,我们可以看出: A 依赖于 B B 依赖于 C C 依赖于 A public class A { private B b; } public class B { private C c; }

  • Spring解决循环依赖的方法(三级缓存)

    说起Spring,作为流水线上装配工的小码农,可能是我们最熟悉不过的一种技术框架.但是对于Spring到底是个什么东西,我猜作为大多数的你可能跟我一样,只知道IOC.DI,却并不明白这其中的原理究竟是怎样的.在这儿你可能想得完整的关于Spring相关的知识,但是我要告诉你对不起.这里不是教程,只能作为你窥探spring核心的窗口.我不做教程,因为网上的教程.源码解析太多,你可以自行选择学习.但我要提醒你的是,看再多的教程也不如你一次的主动去追踪源码. 好了,废话说了这么多就是提醒你这里不是一个教

  • Spring解决循环依赖问题及三级缓存的作用

    目录 前言 1什么是循环依赖 2 如何解决循环依赖 3无法解决的循环依赖 前言 所谓的三级缓存只是三个可以当作是全局变量的Map,Spring的源码中大量使用了这种先将数据放入容器中等使用结束再销毁的代码风格 Spring的初始化过程大致有四步: 创建beanFactory,加载配置文件 解析配置文件转化beanDefination,获取到bean的所有属性.依赖及初始化用到的各类处理器等 刷新beanFactory容器,初始化所有单例bean 注册所有的单例bean并返回可用的容器 我们说的循

  • 浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种:构造器参数循环依赖 表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyIn CreationException异常表示循环依赖. 如在创建TestA类时,构造器需要TestB类,那将去创建TestB,在创建TestB类时又发现需要TestC类,则又去创建Test

  • spring 如何解决循环依赖

    首先解释下什么是循环依赖,其实很简单,就是有两个类它们互相都依赖了对方,如下所示: @Component public class AService { @Autowired private BService bService; } @Component public class BService { @Autowired private AService aService; } AService和BService显然两者都在内部依赖了对方,单拎出来看仿佛看到了多线程中常见的死锁代码,但很显然S

  • 你知道怎么用Spring的三级缓存解决循环依赖吗

    目录 1. 前言 2. Spring Bean的循环依赖 3. Spring中三大循环依赖场景演示 3.1 构造器注入循环依赖 3.2 singleton模式field属性注入循环依赖 3.3 prototype模式field属性注入循环依赖 4. Spring解决循环依赖的原理分析 4.1 Spring创建Bean的流程 4.2 Spring容器的“三级缓存” 4.3 源码解析 4.4 流程总结 5. 总结 1. 前言 循环依赖:就是N个类循环(嵌套)引用. 通俗的讲就是N个Bean互相引用对

  • Spring中循环依赖的解决方法详析

    前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚.本文就试着尽自己所能,对此做出一个较详细的解读.另,需注意一点,下文中会出现类的实例化跟类的初始化两个短语,为怕园友迷惑,事先声明一下,本文的实例化是指刚执行完构造器将一个对象new出来,但还未填充属性值的状态,而初始化是指完成了属性的依赖注入. 一.先说说Spring解决的循环依赖是什么 Java中的循环依赖分两种,一种是构造器的循环依赖,另一种是属性的循环依赖.

随机推荐