Spring三级缓存解决循环依赖?构造方法的循环依赖问题解决(原理、详细过程、面试题)

news2025/1/12 19:58:25

目录

1.什么是循环依赖

2.Spring三级缓存介绍

3.二级缓存可以解决循环依赖吗?

3.1 二级缓存解决循环依赖过程详解

4. 为什么还需要三级缓存呢?/Spring三级缓存的作用?

4.1三级缓存解决循环依赖过程详解

4.2 为什么二级缓存就不能解决代理对象的循环依赖问题呢?/为什么使用三级缓存而不是直接放入二级缓存?

5.构造方法的循环依赖怎么解决?

6. 面试题

6.1 什么是Spring中的循环引用(循环依赖)?

6.2 Spring的一级三级缓存可以解决一般的循环依赖和代理对象的循环依赖,那二级缓存可以去掉吗?/有什么用?

6.3 构造函数出现循环依赖怎么解决?


1.什么是循环依赖

循环依赖是两个或者多个Bean相互持有对方的引用或者自己依赖自己,导致无法进行(属性)依赖注入,造成死循环。

循环依赖分为3种情况

1.A、B相互依赖

2.A、B、C相互依赖

3.A 自己依赖自己

2.Spring三级缓存介绍

一级缓存:存放完全实例化的bean

二级缓存:存放未完全实例化的bean(没有依赖注入的bean)

三级缓存:存放创建bean的工厂(主要为了支持AOP)

3.二级缓存可以解决循环依赖吗?

答:二级缓存可以解决不涉及代理对象的循环依赖问题(但只使用一级缓存不可以),如果涉及代理对象需要用到三级缓存。

一级缓存:限制bean在beanFactory中只存一份,即实现singletonscope单例bean,解决不了循环依赖。

二级缓存:存放未完全实例化的bean,

3.1 二级缓存解决循环依赖过程详解

★下面例子过程详解:

A进行实例化,并且把原始对象(未依赖注入)放入二级缓存中,进行依赖注入时,发现B还没有进行初始化(实例化),所以此时B开始实例化,并且把B的原始对象(未依赖注入)也放入二级缓存中,然后进行依赖注入(A  a),B再把完全初始化的B对象放入一级缓存的单例池中,A进行依赖注入,并且把完全初始化的A对象放入一级缓存的单例池中,放入一级缓存后,二级缓存中的A,B对象都会被清除掉。

4. 为什么还需要三级缓存呢?/Spring三级缓存的作用?

spring三级缓存用来解决代理对象的循环依赖问题。

具体步骤如下:

4.1三级缓存解决循环依赖过程详解

假设我们有两个服务类AB,它们之间存在循环依赖,并且类A需要进行AOP代理。

@Service
public class A {
    @Autowired
    private B b;

    public void methodA() {
        System.out.println("Method A executed");
    }
}

@Service
public class B {
    @Autowired
    private A a;

    public void methodB() {
        System.out.println("Method B executed");
    }
}

同时,我们有一个AOP切面AspectA,它对类AmethodA方法进行了增强。

@Aspect
@Component
public class AspectA {
    @Before("execution(public void com.example.A.methodA())")
    public void beforeA() {
        System.out.println("Before method A executed");
    }
}

具体步骤:

  1. 创建Bean A

    • Spring首先尝试创建Bean A,进行实例化。
    • 在实例化之后,但属性注入之前,Spring将Bean A的工厂对象放入三级缓存中。
  2. 属性注入阶段

    • 在注入Bean B时,发现Bean B依赖于Bean A。
    • 此时,Spring会尝试从一级缓存和二级缓存中获取Bean A,但都未找到。
    • 由于Bean A正在创建中,Spring从三级缓存中获取Bean A的工厂对象,并调用getObject()方法获取Bean A的早期引用(可能是代理对象或原始对象)。
  3. Bean B的创建

    • 使用从三级缓存中获取的Bean A的早期引用,完成Bean B的属性注入。
    • Bean B完成创建后,将其放入一级缓存中。
  4. 完成Bean A的创建

    • 回到Bean A的创建过程,使用Bean B的实例完成Bean A的属性注入。
    • 如果有AOP增强,Spring会在initializeBean(初始化Bean)阶段为Bean A创建代理对象。
    • 将Bean A的代理对象放入一级缓存中,替换掉原始对象。

结果

最终,Bean A和Bean B都能正常创建,并且它们相互引用的是对方的代理对象(如果需要的话)。这样,Spring通过三级缓存机制解决了代理对象的循环依赖问题,同时保持了AOP代理的完整性

看到这里你可能有以下疑问了:

代理对象A刚开始创建实例的时候为什么不把实例化之后属性注入之前的原始对象放入二级缓存,而是把Bean A的工厂对象放入三级缓存中呢?即问题如下
 

4.2 为什么二级缓存就不能解决代理对象的循环依赖问题呢?/为什么使用三级缓存而不是直接放入二级缓存?

1.解决循环依赖:对象B通过获取代理对象A的工厂对象(来创建A的原始对象)来获取A的早期引用(实例化之后,属性注入之前)完成B对象属性注入。

详细如下:

  • 当Bean B依赖于Bean A时,如果Bean A已经完全初始化,那么Bean B可以直接使用。但如果Bean A还没有完全初始化,Spring需要提供一个机制来提前暴露Bean A的引用,以便Bean B可以完成属性注入。
  • 通过将Bean A的工厂对象放入三级缓存,Spring可以在Bean A完全初始化之前,提供一个途径来获取Bean A的早期引用(可能是原始对象或代理对象)。这样,当Bean B需要Bean A时,Spring可以通过工厂对象来获取这个早期引用,完成Bean B的属性注入。

看到这里你可能会问,你也并没有说明为什么非三级缓存不可呢?

别急,请看第二条

2. 延迟代理对象的创建

  • 代理对象应该在所有属性注入完成之后创建,以确保代理对象包含了(被代理对象)所有的代理逻辑。
  • 如果Spring在实例化后立即将Bean A的原始对象放入二级缓存,那么在属性注入阶段,Bean B注入的将是Bean A的原始对象,而不是代理对象。这将导致Bean B中引用的Bean A缺失了AOP代理逻辑。

3.保持Bean生命周期的完整性,确保Bean A在被其他Bean引用之前,已经是一个完全初始化好的对象。

  • Spring容器管理Bean的生命周期,包括实例化、属性注入、初始化等步骤。如果Spring在属性注入之前就将Bean A的原始对象放入二级缓存,那么可能会破坏这个生命周期,因为Bean A还没有完成所有初始化步骤。
  • 通过使用三级缓存,Spring可以在Bean A完成所有初始化步骤后,再决定是否需要将其放入二级缓存或一级缓存。这样可以确保Bean A在被其他Bean引用之前,已经是一个完全初始化好的对象。

spring可以解决所有循环依赖吗?

不可以!

上面代码是通过setter方法完成属性注入的(在Spring框架中,当您使用@Autowired注解时,Spring会自动根据属性名称去寻找相应的setter方法,并调用它来完成依赖注入。)解决的是初始化过程中的循环依赖问题,但是构造方法注入属性时导致的循环依赖怎么解决?

5.构造方法的循环依赖怎么解决?

解决办法:

@Lazy 注解:延迟加载,什么时候需要该对象,就什么时候创建该对象,而不是实例化时把该对象注入进来,即什么时候用,什么时候创建。

6. 面试题

6.1 什么是Spring中的循环引用(循环依赖)?

6.2 Spring的一级三级缓存可以解决一般的循环依赖和代理对象的循环依赖,那二级缓存可以去掉吗?/有什么用?

1.提前曝光的Bean引用

二级缓存用于存放那些已经实例化但还未完全初始化(属性注入未完成)的Bean。这允许在属性注入阶段,如果存在循环依赖,可以提前使用这些Bean的引用。

2.避免重复创建Bean(保证bean是单例的),保持引用一致性

当一个Bean被请求时,如果它正在创建中(即构造函数已调用但属性注入未完成),Spring可以从二级缓存中获取这个Bean的早期引用,而不是重新创建一个新的实例。这避免了循环依赖导致的重复创建问题,确保了所有其他Bean 依赖的正在创建中的Bean 都是同一个实例。

举例:

例如如果二级缓存 不 存放未完全实例化的bean和代理对象,那每次使用代理对象都需要调用代理类的getObjec()方法来获取代理对象实例,可能导致代理对象是多例的。

6.3 构造函数出现循环依赖怎么解决?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在算家云搭建LongWriter(长文创作)

一、 LongWriter 简介 在自然语言处理领域,随着对长文本处理需求的不断增加,能够高效生成长文本的语言模型变得至关重要。LongWriter 的推出正是为了打破传统语言模型在生成超长文本时的限制。LongWriter-glm4-9b 是基于glm-4-9b进行训练的,…

centos7 zabbix监控nginx的pv和uv和status_code

zabbix监控nginx的pv: pv)cat /var/log/nginx/access.log|awk {print $1}|wc -l;;zabbix-get验证: [rootbogon ~]# zabbix_get -s 192.168.253.231 -k pv_uv[pv] 100zabbix监控nginx的uv uv)cat /var/log/nginx/access.log|awk {print $1}|uniq -c | w…

【Oracle实验】字段为空的,无法通过排除判断

Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.场景描述 需求:查询不是某个机构的数据。 同事SQL:where substr(bank_code,1,9) not in(014009001); 看SQL似乎没什么问题,分析…

【SpringCloud】07-分布式事务与Seata

1. 分布式事务 2. Seata 3. 安装seata 配置数据库 CREATE DATABASE IF NOT EXISTS seata /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTIONN */; USE seata;------------------------------- The script used when storeM…

C++ | Leetcode C++题解之第517题超级洗衣机

题目&#xff1a; 题解&#xff1a; class Solution { public:int findMinMoves(vector<int> &machines) {int tot accumulate(machines.begin(), machines.end(), 0);int n machines.size();if (tot % n) {return -1;}int avg tot / n;int ans 0, sum 0;for (…

为什么架构设计禁止IP直连?

什么是IP直连&#xff1f; IP直连指应用程序直接在代码中硬编码IP地址&#xff0c;比如&#xff0c;连接mysql数据库的数据库链接&#xff0c;如下的定义方式&#xff0c;就属于IP直连。 这种写法在开发环境中很常见&#xff0c;但是&#xff0c;在正式生产环境中&#xff0c;…

算法的学习笔记—滑动窗口的最大值(牛客JZ59)

&#x1f600;前言 滑动窗口问题是常见的算法题型&#xff0c;主要考察如何在限定窗口内找到特定值&#xff0c;比如最大值、最小值等。在这篇文章中&#xff0c;我们将深入分析“滑动窗口的最大值”问题的求解方法&#xff0c;并提供一种实现思路&#xff0c;以帮助大家更好地…

惊人,合肥银泰的 LV 柜姐事件引发热议

安徽合肥银泰中心发生的这起“LV柜姐事件”引发了广泛关注。以下是该事件的详细情况&#xff1a;1. 事件起因&#xff1a;- 一名已婚男士到合肥银泰中心的LV专柜购物&#xff0c;因其年轻帅气且出手阔绰&#xff0c;LV柜姐添加了他的联系方式。之后&#xff0c;柜姐在明知男方有…

Python量化交易(五):量化择时策略

引言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年10月学习赛的Python量化交易学习总结文档。在现代金融市场中&#xff0c;量化择时策略已成为投资者追求超额收益的重要工具。本文将系统梳理量化…

电气安全隐患不容忽视

在现代生活中&#xff0c;电力如同血液般滋养着城市的每一个角落&#xff0c;然而&#xff0c;当这股能量失控时&#xff0c;它也能瞬间化身为吞噬生命的火焰。根据国家消防局的权威数据&#xff0c;电气火灾占所有火灾的28.4%&#xff0c;而其中6.5%的源头直指短路——这一看似…

单细胞数据分析(三):单细胞聚类分析

文章目录 介绍目的加载R包数据链接导入数据可视化聚类选择聚类分辨率值输出结果系统信息介绍 单细胞聚类分析是一种用于解析单细胞RNA测序(scRNA-seq)数据的方法,旨在将成千上万个单细胞根据它们的基因表达模式分组到不同的类别或“簇”中。每个簇代表了具有相似基因表达特…

auto占位符(C++11~C++17)

文章目录 1. 定义1.1 注意事项 2. 推导规则3. 返回类型推导(C14)4. lambda表达式中使用auto类型推导5. 非类型模板形参占位符&#xff08;C17&#xff09; 1. 定义 在C11以前&#xff0c;auto关键字是用来声明自动变量的。从C11起auto被用来&#xff1a;声明变量时根据初始化表…

推荐收藏!一款超好用的网盘资源搜索工具

抖知书老师推荐&#xff1a; 盘搜问世以来&#xff0c;以其简洁、实用的设计&#xff0c;迅速成为网民心中的网盘资源搜索利器。对于那些日常生活离不开网盘的用户来说&#xff0c;盘搜的存在让他们的资源查找变得更加便捷和高效。从影视资源到课程资料再到个人文件&#xff0…

opencv - py_imgproc - py_filtering filtering 过滤-卷积平滑

文章目录 平滑图像目标2D 卷积&#xff08;图像过滤&#xff09;图像模糊&#xff08;图像平滑&#xff09;1. 平均2. 高斯模糊3. 中值模糊4. 双边滤波 其他资源 平滑图像 目标 学习&#xff1a; 使用各种低通滤波器模糊图像将定制滤波器应用于图像&#xff08;2D 卷积&…

MT1401-MT1410 码题集 (c 语言详解)

目录 MT1401归并排序 MT1402堆排序 MT1403后3位排序 MT1404小大大小排序 MT1405小大大小排序II MT1406数字重排 MT1407插入 MT1408插入 MT1409旋转数组 MT1410逆时针旋转数组 MT1401归并排序 c 语言实现代码 #include <stdio.h>// merge two subarrays void merge(int a…

大数据日志处理框架ELK方案

介绍应用场景大数据ELK日志框架安装部署 一&#xff0c;介绍 大数据日志处理框架ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;是一套完整的日志集中处理方案&#xff0c;以下是对其的详细介绍&#xff1a; 一、Elasticsearch&#xff08;ES&#xff09; 基本…

PHP海外矿物矿机理财投资源码-金融理财投资源码

PHP海外矿物矿机理财投资源码/金融理财投资源码 海外矿物矿机理财投资源码 测试不错,可以做其他产品理财,功能都没啥太大问题

Unity3D学习FPS游戏(1)获取素材、快速了解三维模型素材(骨骼、网格、动画、Avatar、材质贴图)

前言&#xff1a;最近重拾Unity&#xff0c;准备做个3D的FPS小游戏&#xff0c;这里以官方FPS案例素材作为切入。 导入素材和素材理解 安装Unity新建项目新建文件夹和Scene如何去理解三维模型素材找到模型素材素材预制体结构骨骼和网格材质&#xff08;Material&#xff09;、…

php反序列化漏洞典型例题

1.靶场环境 ctfhub-技能树-pklovecloud 引用题目&#xff1a; 2021-第五空间智能安全大赛-Web-pklovecloud 2.过程 2.1源代码 启动靶场环境&#xff0c;访问靶场环境&#xff0c;显示源码&#xff1a;直接贴在下面&#xff1a; <?php include flag.php; class pks…

【flask】 flask redis的使用

目的&#xff1a;如何使用在flask web项目中连接redis&#xff0c;并简单的使用 使用的库包&#xff1a;flask-redis pip install falsk-redis下面的写法是对项目代码进行模块化拆分的写法&#xff0c;在app.py中只进行对象的初始化等操作&#xff1b;exts.py中创建对象&…