Spring为何要用三级缓存来解决循环依赖问题

 更新时间:2020年10月25日 15:03:45   作者:zero  
这篇文章主要给大家介绍了关于Spring为何要用三级缓存来解决循环依赖问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

我们都知道Spring为了解决循环依赖使用了三级缓存

Spring三级缓存

一级缓存singletonObjects

用于保存BeanName和创建bean实例之间的关系,beanName -> bean instance

private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

二级缓存earlySingletonObjects

保存提前曝光的单例bean对象

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三级缓存singletonFactories

保存beanName和创建bean实例之间的关系,与singletonObjects不同的地方在于,当一个单例bean被放到这里面后,bean在创建过程中,可以通过getBean方法获取到,目的是用来检测循环引用

private final Map<String, Object> singletonFactories = new HashMap(16);

在创建bean的时候,首先从缓存中获取单例的bean,这个缓存就是singletonObjects,如果获取不到且bean正在创建中,就再从earlySingletonObjects中获取,如果还是获取不到且允许从singletonFactories中通过getObject拿到对象,就从singletonFactories中获取,如果获取到了就存入earlySingletonObjects并从singletonFactories中移除。

为什么要使用三级缓存?

一级缓存

首先我们看看一级缓存行不行,如果只留第一级缓存,那么单例的Bean都存在singletonObjects 中,Spring循环依赖主要基于Java引用传递,当获取到对象时,对象的field或者属性可以延后设置,理论上可以,但是如果延后设置出了问题,就会导致完整的Bean和不完整的Bean都在一级缓存中,这个引用时就有空指针的可能,所以一级缓存不行,至少要有singletonObjects 和earlySingletonObjects 两级。

两级缓存

那么我们再看看两级缓存行不行

现在有A的field或者setter依赖B的实例对象,同时B的field或者setter依赖了A的实例,A首先开始创建,并将自己暴露到 earlySingletonObjects 中,开始填充属性,此时发现自己依赖B的属性,尝试去get(B),发现B还没有被创建,所以开始创建B,在进行属性填充时初始化A,就从earlySingletonObjects 中获取到了实例化但没有任何属性的A,B拿到A后完成了初始化阶段,将自己放到singletonObjects中,此时返回A,A拿到B的对象继续完成初始化,完成后将自己放到singletonObjects中,由A与B中所表示的A的属性地址是一样的,所以A的属性填充完后,B也获取了A的属性,这样就解决了循环的问题。

似乎完美解决,如果就这么使用的话也没什么问题,但是再加上AOP情况就不同了,被AOP增强的Bean会在初始化后代理成为一个新的对象,也就是说:

如果有AOP,A依赖于B,B依赖于A,A实例化完成暴露出去,开始注入属性,发现引用B,B开始实例化,使用A暴露的对象,初始化完成后封装成代理对象,A再将代理后的B注入,再做代理,那么代理A中的B就是代理后的B,但是代理后的B中的A是没用代理的A。

显然这是不对的,所以在Spring中存在第三级缓存,在创建对象时判断是否是单例,允许循环依赖,正在创建中,就将其从earlySingletonObjects中移除掉,并在singletonFactories放入新的对象,这样后续再查询beanName时会走到singletonFactory.getObject(),其中就会去调用各个beanPostProcessor的getEarlyBeanReference方法,返回的对象就是代理后的对象。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
  implements AutowireCapableBeanFactory { 
 
  // Eagerly cache singletons to be able to resolve circular references
  // even when triggered by lifecycle interfaces like BeanFactoryAware.
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
   if (logger.isDebugEnabled()) {
    logger.debug("Eagerly caching bean '" + beanName +
      "' to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  }

  /**
  * Add the given singleton factory for building the specified singleton
  * if necessary.
  * <p>To be called for eager registration of singletons, e.g. to be able to
  * resolve circular references.
  * @param beanName the name of the bean
  * @param singletonFactory the factory for the singleton object
  */
 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);
   }
  }
 }

总结

到此这篇关于Spring为何要用三级缓存来解决循环依赖问题的文章就介绍到这了,更多相关Spring用三级缓存解决循环依赖内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringCloud Gateway网关功能介绍与使用

    SpringCloud Gateway网关功能介绍与使用

    SpringCloud Gateway 是 Spring Cloud 的一个全新项目,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。这篇文章主要介绍了SpringCloud Gateway网关作用,需要的朋友可以参考下
    2022-12-12
  • springboot解决前后端分离时的跨域问题

    springboot解决前后端分离时的跨域问题

    这篇文章主要介绍了springboot如何解决前后端分离时的跨域问题,帮助大家更好的理解和学习使用springboot,感兴趣的朋友可以了解下
    2021-04-04
  • 基于Java实现互联网实时聊天系统(附源码)

    基于Java实现互联网实时聊天系统(附源码)

    Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。本文将利用它实现互联网实时聊天系统,感兴趣的可以了解一下
    2022-09-09
  • Java操作数据库(行级锁,for update)

    Java操作数据库(行级锁,for update)

    这篇文章主要介绍了Java操作数据库(行级锁,for update),文章围绕Java操作数据库的相关资料展开详细内容,需要的小伙伴可以参考一下,希望对你有所帮助
    2021-12-12
  • mybatis中如何实现一个标签执行多个sql语句

    mybatis中如何实现一个标签执行多个sql语句

    这篇文章主要介绍了mybatis中如何实现一个标签执行多个sql语句问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • Fluent Mybatis实际开发中的优势对比

    Fluent Mybatis实际开发中的优势对比

    本文给大家介绍如何通过IQuery和IUpdate定义强大的动态SQL语句,给大家分享Fluent Mybatis实际开发中的优势讲解,感兴趣的朋友一起看看吧
    2021-08-08
  • 如何在Java SpringBoot项目中配置动态数据源你知道吗

    如何在Java SpringBoot项目中配置动态数据源你知道吗

    这篇文章主要介绍了SpringBoot如何在运行时动态添加数据源,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2021-09-09
  • Maven中的dependencyManagement 实例详解

    Maven中的dependencyManagement 实例详解

    dependencyManagement的中文意思就是依赖关系管理,它就是为了能通更好统一管理项目的版本号和各种jar版本号,可以更加方便升级,解决包冲突问题,这篇文章主要介绍了Maven中的dependencyManagement 实例详解,需要的朋友可以参考下
    2024-02-02
  • Spring对静态变量无法注入的解决方案

    Spring对静态变量无法注入的解决方案

    这篇文章主要介绍了使用Spring对静态变量无法注入的解决方案,具有很好的参考价值,希望对大家有所帮助。
    2021-07-07
  • Java中的接口和抽象类用法实例详解

    Java中的接口和抽象类用法实例详解

    这篇文章主要介绍了Java中的接口和抽象类用法,结合实例形式较为详细的分析了Java中关于接口和抽象类的概念、定义、用法与相关注意事项,需要的朋友可以参考下
    2015-12-12

最新评论