Spring之循环依赖

什么是循环依赖?

依赖的相互引用,如下列的这种形式

@Component
public class A {

    @Autowired
    private B b;
    
}

@Component
public class B {

    @Autowired
    private A a;
}

Spring是如何解决循环依赖的

Spring是通过三级缓存来解决循环依赖

  • singletonObjects : 单例bean,已经实例化,完成属性填充
  • earlySingletonObjects : 半成品bean,已经实例化,未完成属性填充
  • singletonFactories : 一个函数式接口

为什么要使用三级缓存?

因为Spring的动态代理流程是在属性填充流程之后的,如果只使用二级缓存,半成品bean中不确定是放一个普通对象还是代理对象,所以需要使用三级缓存来保存一个不确定的值

PS : 如果只使用二级缓存,那必须所有对象都进行动态代理,那样会生成很多class文件,浪费内存

图解一般情况下的循环依赖

Spring真的能完全解决循环依赖? 

改造一下上文代码

@Component
public class A {

    private B b;

    public A(B b) {
        this.b = b;
    }
}

@Component
public class B {

    @Autowired
    private A a;
}

很遗憾,Spring没能解决这种构造器与setter注入形式的循环依赖,我们来分析具体原因

前置知识点

  1. 如果一个类有且仅有一个有参构造方法,Spring会以此构造方法实例化对象
  2. 实例化对象之前,会先处理参数的依赖。比如上述代码中bean A的创建过程中,会先getBean(b),再实例化对象A

源码分析

AbstractBeanFactory#doGetBean

我们先看一下这个getSingleton方法本身,而不是createBean方法

由上述源码我们可以得知:执行createBean方法之前会将beanName加入到singletonsCurrentlyInCreation列表中,表明这个bean正在创建

后续b进行属性填充的时候会先从singletonFactories中获取a,因为a还没有进行实例化,没有放入singletonFactories中,所以又会执行getSingleton方法,因为singletonsCurrentlyInCreation这个列表已经存在a这个beanName,所以if条件成立,抛出异常

图解流程

构造方法与setter注入的循环依赖,如果构造方法对应的bean先处理,则会抛出异常,后处理则不会抛出异常。

注意 : bean A 还没有实例化,singletonFactories​​中还没有提前暴露的对象

如果将上述代码改下列形式,则不会抛出异常

@Component
public class A {
    
    @Autowired
    private B b;
}

@Component
public class B {

    private A a;

    public B(A a) {
        this.a = a;
    }
}

PS :针对A先实例化,B后实例化 

综上所述

  • setter和setter的循环依赖:正确注入
  • 构造方法和setter的循环依赖:抛出异常
  • setter和构造方法的循环依赖:正确注入
  • 构造方法和构造方法的循环依赖:抛出异常

普通情况下,我们标记bean正在创建,后续会将实例对象提前暴露放入singletonFactories中,但是构造方法和setter方式的循环依赖在标记bean正在创建后,并没有立刻将实例对象放入singletonFactories中,而是处理构造方法中参数所对应的依赖,如果这个参数对应的依赖,循环依赖了当前bean,则会抛出异常。即标记正在创建的bean,在对象提前暴露放入singletonFactories之前,不能注入一个依赖当前bean的bean

Spring如何解决循环依赖?

SpringBoot2.6及以上版本默认不支持循环依赖,那如何解决循环依赖呢?

主要原因就是SpringBoot将allowCircularReferences默认值设置为false

从配置角度

1.在application.properties文件中将该值设置为true

spring.main.allow-circular-references=true

2.使用bfpp将该值设置为true

@Component
public class FirstBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) registry;
        defaultListableBeanFactory.setAllowCircularReferences(true);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
从代码角度

1.使用@Lazy注解

@Component
public class A {

    @Autowired
    @Lazy
    private B b;
    
}

@Component
public class B {

    @Autowired
    private A a;

}

2.使用@Lookup注解

@Component
public class A {

    private B b;

    @Lookup
    public B getB() {
       return this.b;
    }
}

@Component
public class B {

    @Autowired
    private A a;

}

3.实现ApplicationContextAware接口

@Component
public class A implements ApplicationContextAware {

    private B b;

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        this.b = applicationContext.getBean(B.class);
    }

}

@Component
public class B {

    @Autowired
    private A a;

}

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

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

相关文章

非周期连续函数的傅里叶变换

首先 我们把一个非周期信号扩展成一个周期信号 然后用傅里叶级数展开 也可以得到对应的级数系数 利用周期趋向于无穷大 可以把傅里叶级数展开就变成了一个积分 而神奇的是积分里其实还有一个积分 这样我们就得到了傅里叶变换对 我们把里面的积分成为函数的傅里叶变换 把外面…

C语言交换二进制位的奇数偶数位

基本思路 我们要先把想要交换的数的二进制位给写出来假如交换13的二进制位,13的二进制位是 0000 0000 0000 0000 0000 0000 0000 1101然后写出偶数位的二进制数(偶数位是1的) 1010 1010 1010 1010 1010 1010 1010 1010然后写出奇数位的二进…

Mac通过Idea启动Tomcat时出现Cannot run program xxx/catalina.sh 错误

问题描述 Mac通过Idea启动Tomcat时出现Cannot run program xxx/catalina.sh错误,详细如下: Error running Tomcat9 Cannot run program "/Users/xxx/Desktop/work/devtools/apache-tomcat-9.0.19/bin/catalina.sh" (in directory "/U…

算法设计课第一周(排序算法的效率分析)

实验1 排序算法的效率分析 一、【实验目的】 (1)复习排序算法的实现过程; (2)设计平均与最坏情况下时间复杂度的数据环境并理解相关含义; (3)初步了解算法时间复杂度的分析方法。…

FlashFace:一种高保真身份保存的人类形象个性化方法

FlashFace技术是由香港大学、阿里巴巴集团、蚂蚁集团共同研发的一项实用工具,用户可以通过提供一张或几张参考面部图像和文本提示,就可以轻松地即时个性化自己的相片。 与现有的人像定制方法相比,FlashFace方法具有更高保真度的身份保留xi性…

【spring】Spring Cloud Alibaba版本发布说明

Spring Cloud Alibaba版本发布说明 由于 Spring Boot 3.0,Spring Boot 2.7~2.4 和 2.4 以下版本之间变化较大,目前企业级客户老项目相关 Spring Boot 版本仍停留在 Spring Boot 2.4 以下,为了同时满足存量用户和新用户不同需求,社…

Vue 组件化编程

Vue 组件化编程 非单文件组件 定义组件 使用Vue.extend(options)创建 不要写eldata要写成函数,避免组件被复用时,数据存在引用关系 注册组件 局部注册:new Vue()的时候,options传入components全局注册:V…

Linux 个人笔记之三剑客 grep sed awk

文章目录 零、预一、grep 文本过滤工具基础篇实战篇 二、sed 字符流编辑器基础篇实战篇 三、awk 文本处理工具基础篇实战篇 四、附xargsuniq & sort基础篇实战篇 cut 零、预 bash 的命令行展开 {} $ echo file_{1..4} file_1 file_2 file_3 file_4$ echo file_{a..d} file_…

高等数学基础篇(数二)之多元函数的基本概念

多元函数基本概念: 一、多元函数的极限 二、多元函数的连续性 三、偏导数 四、全微分 目录 一、多元函数的极限 二、多元函数的连续性 三、偏导数 1.偏导数的定义 2.二元函数偏导数的几何意义 3.高阶偏导数 四、全微分 补充: 一元函数极限连…

BIONIOAIO

通信技术整体解决的问题 1.局域网内的通信要求 2.多系统间的底层消息传递机制 3.高并发下,大数据量的通信场景需要 4.游戏行业。无论是手游服务端、还是大型网络游戏,java的应用越来越广 IO模型基本说明 就是用什么样的通道或者说是通信模式和架构…

数学逻辑专题

数学专题 分治篇LeetCode 263. 丑数解题思路代码实现总结 不要纠结,干就完事了,熟练度很重要!!!多练习,多总结!!! 分治篇 LeetCode 263. 丑数 解题思路 为判断 nnn 是否…

前端实现菜单搜索搜索(功能模版)

目录 前言正文 前言 总体界面如下所示&#xff1a; 正文 <template><div class"avue-searchs"click.self"handleEsc"><div class"avue-searchs__title">菜单搜索</div><div class"avue-searchs__content"…

以XX医院为例的医疗建筑能效管理系统【建筑能耗 供电可靠 】

一、行业背景 二、行业特点 1.供电可靠性要求高&#xff1a;医院配电系统复杂&#xff0c;门诊、急救、手术室、ICU/CCU、血液透析等场合特一级和一级负荷比较多&#xff0c;一旦发生故障会造成严重影响&#xff0c;对配电可靠性要求极高。 2.能耗水平高&#xff1a;医院能耗…

Vue.js基础指令

&#xff08;在讲指令之前&#xff0c;可以先了解插值表达式&#xff0c;如果已经知道&#xff0c;当我没说&#xff09; 一.插值表达式 1.数据绑定最常见的形式就是双大括号的文本插值&#xff0c;Mustache上属性的值替代。只要绑定的数据对象上属性发生了改变&#xff0c;插…

10_MVC

文章目录 JSON常用的JSON解析Jackson的常规使用指定日期格式 MVC设计模式MVC介绍前后端分离案例&#xff08;开发与Json相关接口&#xff09; 三层架构三层架构介绍 JSON JSON&#xff08;JavaScript Object Notation&#xff09; 是一种轻量级的数据交换格式&#xff0c;是存…

【教学类-09-03】20240401细线迷宫图02(A4横版一页-2份竖版)

作品展示&#xff1a; 背景需求&#xff1a; 【教学类-09-02】20240331细线迷宫图01&#xff08;A4横版一页1份横版&#xff09;-CSDN博客文章浏览阅读779次&#xff0c;点赞28次&#xff0c;收藏6次。【教学类-09-02】20240331细线迷宫图01&#xff08;A4横版一页1份横版&…

【2012 统考真题/完整代码】找单词共同后缀的起始位置

题目 假定采用带头结点的单链表保存单词&#xff0c;当两个单词有相同的后缀时&#xff0c;则可共享相同的后缀存储空间&#xff0c;例如&#xff0c;“loading”和“being”的存储映像如下图所示。 设str1和str2分别指向两个单词所在单链表的头结点&#xff0c;链表结点结构为…

PTA L2-043 龙龙送外卖

龙龙是“饱了呀”外卖软件的注册骑手&#xff0c;负责送帕特小区的外卖。帕特小区的构造非常特别&#xff0c;都是双向道路且没有构成环 —— 你可以简单地认为小区的路构成了一棵树&#xff0c;根结点是外卖站&#xff0c;树上的结点就是要送餐的地址。 每到中午 12 点&#…

【Linux】权限理解

权限理解 1. shell命令以及运行原理2. Linux权限的概念3. Linux权限管理3.1 文件访问者的分类&#xff08;人&#xff09;3.2 文件类型和访问权限&#xff08;事物属性&#xff09;3.2.1 文件类型3.2.2 基本权限 3.3 文件权限值的表示方法3.4 文件访问权限的相关设置方法3.4.1 …

Taskflow应用:波前并行(Wavefront Parallelism)

2D的Wavefront如下图所示&#xff1a; #include <taskflow/taskflow.hpp>std::mutex cout_mutex; void format_str(std::string const& str) {std::lock_guard<std::mutex> lock(cout_mutex); std::cout << str << std::endl; }std::string crea…