spring解决循环依赖

 更新时间:2020年09月29日 14:21:19   作者:TomCoding  
这篇文章主要介绍了spring如何解决循环依赖,帮助大家更好的理解和使用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 循环依赖的资料请关注脚本之家其它相关文章!

相关文章

  • Spring init-method与destroy-method属性的用法解析

    Spring init-method与destroy-method属性的用法解析

    这篇文章主要介绍了Spring init-method与destroy-method属性的用法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java BigDecimal中divide方法案例详解

    Java BigDecimal中divide方法案例详解

    这篇文章主要介绍了Java BigDecimal中divide方法案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • SpringCloud 如何提取公共配置

    SpringCloud 如何提取公共配置

    这篇文章主要介绍了SpringCloud 提取公共配置的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • kafka生产者和消费者的javaAPI的示例代码

    kafka生产者和消费者的javaAPI的示例代码

    这篇文章主要介绍了kafka生产者和消费者的javaAPI的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • SpringCloud之Feign远程接口映射的实现

    SpringCloud之Feign远程接口映射的实现

    这篇文章主要介绍了SpringCloud之Feign远程接口映射的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 全面了解servlet中cookie的使用方法

    全面了解servlet中cookie的使用方法

    下面小编就为大家带来一篇全面了解servlet中cookie的使用方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • spring profile 多环境配置管理详解

    spring profile 多环境配置管理详解

    这篇文章主要介绍了 spring profile 多环境配置管理详解的相关资料,需要的朋友可以参考下
    2017-01-01
  • 浅谈Java中hashCode的正确求值方法

    浅谈Java中hashCode的正确求值方法

    这篇文章主要介绍了浅谈Java中hashCode的正确求值方法,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Spring RestTemplate具体使用详解

    Spring RestTemplate具体使用详解

    这篇文章主要介绍了Spring RestTemplate具体使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-04-04
  • Java CyclicBarrier错误使用的实例

    Java CyclicBarrier错误使用的实例

    文章描述了一个Java程序使用CyclicBarrier实现两个线程交替打印字母和数字时遇到的问题,由于线程执行顺序不当,导致输出结果与预期不符,通过调整线程的等待和打印顺序,解决了问题,文章还提供了一个控制流图来详细解释修改前后的程序逻辑
    2025-01-01

最新评论