spring的循环依赖和三级缓存

  1. 什么是循环依赖

    1. A引用了B,而这个时候B也引用了A,那么这种情况实际上就是出现了循环依赖的问题了,实际上也可以把循环依赖称之为循环引用,两个或者两个以上的bean互相持有对方,最终形成闭环
    2. 这里不是函数的循环调用,是对象的相互依赖关系,循环调用其实就是一个死循环,除非有终结条件,否则的话,他就是一个死循环
  2. spring的循环依赖

    1. spring的循环依赖有:
      1. 构造器的循环依赖
      2. field属性的循环依赖
    2. 构造器的循环依赖实际上是个无解的操作,只能抛出BeanCurrentlyInCreationException异常,也就是说,这个构造器导致的循环依赖,spring是没有办法来处理的,也只是给抛出了异常
  3. spring解决循环依赖

    1. spring的单例对象的初始化主要分为三个步骤
      1. createBeanInstance 实例化
      2. populateBean 填充属性
      3. initializeBean 初始化
    2. createBeanInstance实例化实际上就是调用对象的构造方法实例化对象
    3. populateBean实际上就是对bean的依赖属性进行一个赋值填充
    4. initializeBean则是调用spring xml中的int方法
    5. 但从bean的初始化的过程来看,循环依赖发生的位置就是在createBeanInstance实例化,以及populateBean填充属性中
    6. 发生的循环依赖是:构造器的循环依赖和field属性的循环依赖
  4. 三级缓存

    1. singletonFactories:单例对象工厂的cache,用于存放完全初始化好的bean,从该缓存中取出的bean可以直接使用

    2. earlySingletonObjects:提前曝光的单例对象的cache,存放原始的bean对象(尚未填充属性),用于解决循环依赖

    3. singletonObjects:单例对象的cache,存放bean工厂对象,用于解决循环依赖

      1. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
        private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
        
  5. 对象创建过程

    1. AbstractBeanFactory 中的 doGetBean()方法

      1. protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)
        
    2. DefaultSingletonBeanRegistry 中的 getSingleton()方法

      1. protected Object getSingleton(String beanName, boolean allowEarlyReference)
        
      2. 在这个方法中,先从一级缓存singletonObjects中去获取,如果获取到就直接return

      3. 如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取

      4. 如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()获取

      5. 如果获取到了,则从singletonFactories中移除,并放入earlySingletonObjects中

      6. 这相当于把三级缓存中的数据剪贴到了二级缓存中

      7. 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;
         }
        
    3. AbstractAutowireCapableBeanFactory 中的 doCreateBean() 方法

      1. protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        
         //添加到三级缓存
          if (earlySingletonExposure) {
           if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
              "' to allow for resolving potential circular references");
           }
           addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
          }
        
    4. AbstractAutowireCapableBeanFactory 中的 populateBean() 方法进行属性赋值

      1. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)
        
    5. AbstractAutowireCapableBeanFactory 中的 initializeBean() 初始化对象

      1. protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
        
  6. 解决

    1. spring解决循环依赖的诀窍就在于singletonFactories这个三级cache
    2. 这个cache的类型是ObjectFactory,这里就是解决循环依赖的关键,发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)
    3. 这哦对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以spring此时将这个对象提前曝光出来让大家认识,让大家使用
  7. 二级缓存到底能不能解决

    1. 二级缓存也是能够实现的,如果自己想要实现,那么就得重写AbstractAutowireCapableBeanFactory的doCreateBean的方法

    2.      //添加到三级缓存
        if (earlySingletonExposure) {
         if (logger.isTraceEnabled()) {
          logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
         }
         addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
         //从三级缓存中取出立刻放入二级缓存
         getSingleton(beanName, true);
        }
      
    3. 如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了spring的设计原则

    4. spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postPocessAfterInitialization方法中对初始化后的Bean完成AOP代理

    5. 如果出现了循环依赖,那没有办法,只能给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/780843.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从一个(模型设计的)想法到完成模型验证的步骤

从有一个大型语言模型&#xff08;LLM&#xff09;设计的想法到完成该想法的验证&#xff0c;可以遵循以下实践步骤&#xff1a; 需求分析&#xff1a; 明确模型的目的和应用场景。确定所需的语言类型、模型大小和性能要求。分析目标用户群体和使用环境。 文献调研&#xff1a…

【全面讲解下iPhone新机官网验机流程】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

实现多数相加,但是传的参不固定

一、情景 一般实现的加法和减法等简单的相加减函数的话。一般都是写好固定传的参数。比如&#xff1a; function add(a,b) {return a b;} 这是固定的传入俩个&#xff0c;如果是三个呢&#xff0c;有人说当然好办&#xff01; 这样写不就行了&#xff01; function add(a…

protobuf及其使用

首先打开proto文件&#xff0c;定义一个类&#xff08;数据结构&#xff09;&#xff0c;并编写成员变量 使用protobuf编译器protoc编译proto文件为.pb.h和.pb.c文件(c) 看绿色注释部分&#xff1a;从左至右为&#xff0c;编译器&#xff0c;.proto文件的路径&#xff0c;编译的…

YOLO V7网络实现细节(2)—网络整体架构总结

YOLO V7网络整体架构总结 YOLO v7网络架构的整体介绍 不同GPU和对应模型&#xff1a; ​​​​​​​边缘GPU&#xff1a;YOLOv7-tiny普通GPU&#xff1a;YOLOv7​​​​​​​云GPU的基本模型&#xff1a; YOLOv7-W6 激活函数&#xff1a; YOLOv7 tiny&#xff1a; leaky R…

微深节能 煤码头自动化翻堆及取料集控系统 格雷母线

微深节能格雷母线高精度位移测量系统是一种先进的工业自动化位置检测解决方案&#xff0c;它被广泛应用于煤码头自动化翻堆及取料集控系统中&#xff0c;以实现对斗轮堆取料机等大型机械设备的精准定位和自动化控制。 系统原理简述&#xff1a; 格雷母线系统的工作原理基于电磁…

有趣的算法

目录&#xff1a; 1、百钱买百鸡 2、韩信点兵 1&#xff09;概述 2&#xff09;正常取余算法 3&#xff09;循环算法 1、百钱买百鸡 我国古代《算经》中的“百钱买百鸡”问题&#xff1a; 鸡翁一&#xff0c;值钱五&#xff1b;鸡母一&#xff0c;值钱三&#xff1b;鸡…

机器学习第四十六周周报 FMP

文章目录 week46 FMP摘要Abstract1. 题目2. Abstract3. FMP3.1 优化框架3.2 优化器 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程 5. 结论6.代码复现1. FMP2. fairGNN小结参考文献 week46 FMP 摘要 本周阅读了题为Chasing Fairness in Graphs: A GNN Architecture Per…

初识java—jdk17的一些新增特性

文章目录 前言一 &#xff1a; yield关键字二 &#xff1a;var关键字三 &#xff1a;密封类四 &#xff1a;空指针异常&#xff1a;五&#xff1a;接口中的私有方法&#xff1a;六&#xff1a;instanceof关键字 前言 这里介绍jdk17相对于jdk1.8的部分新增特性。 一 &#xff…

Spring Boot的无缝衔接:深入解析与实践

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ &#x1f680;The begin&#x1f697;点点关注&#xff0c;收藏不迷路&#x1f6a9; 引言 在快速迭代的软件开发环境中&#xff0c;无缝衔接是提升开发效率、降低维护成本、增强系统稳定性的关键。Spring Boo…

STM32芯片系列与产品后缀解读

一. 产品系列 STM32单片机是一系列基于ARM Cortex-M内核的32位微控制器&#xff0c;广泛应用于嵌入式系统中。 STM32系列由STMicroelectronics&#xff08;意法半导体&#xff09;开发和生产&#xff0c;并凭借其灵活的设计、丰富的外设和强大的生态系统&#xff0c;成为嵌入式…

LLM - 卷积神经网络(CNN)

1. 卷积神经网络结构&#xff1a;分为输入层&#xff0c;卷积层&#xff0c;池化层&#xff0c;全连接层&#xff1b; &#xff08;1&#xff09;首先进入输入层&#xff0c;对数据数据进行处理&#xff0c;将输入数据向量化处理&#xff0c;最终形成输入矩阵。 &#xff08;…

C++ 什么是虚函数?什么是纯虚函数,以及区别?(通俗易懂)

&#x1f4da; 当谈到虚函数时&#xff0c;通常是指在面向对象编程中的一种机制&#xff0c;它允许在派生类中重写基类的函数&#xff0c;并且能够通过基类指针或引用调用派生类中的函数。 目录 前言 &#x1f525; 虚函数 &#x1f525; 纯虚函数 &#x1f525; 两者区别…

用 Echarts 画折线图

https://andi.cn/page/621503.html

leetcode每日一题-3033. 修改矩阵

题目描述&#xff1a; 解题思路&#xff1a;简单题目&#xff0c;思路非常直接。对列进行遍历&#xff0c;记录下最大值&#xff0c;然后再遍历一遍&#xff0c;把-1替换为最大值。需要注意的是进行列遍历和行遍历是不同的。 官方题解&#xff1a; class Solution { public:v…

VRay渲染有什么技巧?渲染100邀请码1a12

渲染是视觉行业非常重要的一环&#xff0c;没有渲染就没有效果图&#xff0c;常用的渲染器有Vray&#xff0c;而Vray渲染有很多技巧&#xff0c;可以让渲染更快更省&#xff0c;下面我们总结下。 1、删除无用对象 检查场景&#xff0c;看是否有一些不需要渲染的物体和灯光&am…

将大型语言模型模块化打造协作智能体

B UILDING C OOPERATIVE E MBODIED A GENTS MODULARLY WITH L ARGE L ANGUAGE M ODELS 论文链接&#xff1a; https://arxiv.org/abs/2307.02485https://arxiv.org/abs/2307.02485 1.概述 在去中心化控制及多任务环境中&#xff0c;多智能体合作问题因原始感官观察、高昂…

绝区肆--2024 年AI安全状况

前言 随着人工智能系统变得越来越强大和普及&#xff0c;与之相关的安全问题也越来越多。让我们来看看 2024 年人工智能安全的现状——评估威胁、分析漏洞、审查有前景的防御策略&#xff0c;并推测这一关键领域的未来可能如何。 主要的人工智能安全威胁 人工智能系统和应用程…

el-date-picker 设置默认值为当前日期

this.listQuery.Date new Date().toISOString().substr(0, 10); <el-date-picker v-model"listQuery.Date" format"yyyy-MM-dd" value-format"yyyy-MM-dd" type"date" placeholder"选择日期" change"getList()&qu…

Java语言程序设计篇一

Java语言概述 Java语言起源编程语言最新排名名字起源Java语言发展历程Java语言的特点Java虚拟机垃圾回收Java语言规范Java技术简介Java程序的结构Java程序注意事项&#xff1a;注释编程风格练习 Java语言起源 1990年Sun公司提出一项绿色计划。1992年语言开发成功最初取名为Oak…