面试官 问一级缓存与二级缓存

本文介绍了缓存的基本原理,包括一级缓存与二级缓存的区别及其应用场景。通过对比数据库的IO操作速度,阐述了缓存如何有效减轻数据库的压力,并分别解释了Hibernate与MyBatis中缓存的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

老哥的经验:

缓存为了减轻数据库访问量;

一级比二级多了一级

一级缓存请求内存,没有的话在请求数据库;

二级缓存请求内存,没有在请求二级缓存区,没有在请求数据库;

Hibernate 二级缓存需要添加配置文件

redis 自带二级缓存

因为数据库去进行IO操作(增删更新)都需要像(唱片)的刻度一样,动刻度,非常慢,

所以需要缓存减轻数据库访问量达到什么减轻数据库压力等等作用;

在看看别人的标准答案:

hibernate一级缓存和二级缓存的区别: https://blog.youkuaiyun.com/defonds/article/details/2308972

MyBatis缓存分为一级缓存和二级缓存:https://blog.youkuaiyun.com/u014756827/article/details/52754750

<think>嗯,我现在得理解Spring是怎么通过三级缓存解决循环依赖的题。首先,我得回忆一下什么是循环依赖。循环依赖就是两个或多个Bean相互依赖,比如A依赖B,B又依赖A。这种情况在Spring中如果不处理的话,可能会导致无限递归或者Bean创建失败。 那Spring的三级缓存具体是怎么解决这个题的呢?我记得三级缓存是三个不同的Map,用来存储Bean的不同状态。但具体每个缓存存的是什么,可能有点模糊。比如,一级缓存存放的是完整的Bean实例,二级缓存存放的是早期暴露的对象,三级缓存存放的是Bean工厂?或者是不是三级缓存有不同的分工? 接下来,我需要想一下Bean的创建过程。当Spring开始创建一个Bean的时候,比如Bean A,它会先实例化A(通过构造函数),然后进行属性填充。这时候如果A依赖B,Spring就会去创建B。同样,B在创建过程中如果又依赖A,就会再次尝试获取A。这时候如果A还没有完全创建好,就会形成循环依赖。 这里的关键点可能是在实例化之后,Spring会提前将Bean的引用暴露出来,这样其他Bean在依赖注入时可以使用这个早期的引用。但是如何保证这个时候的Bean虽然还没有完成属性填充和初始化,但已经可以被引用呢? 这时候三级缓存的作用可能就体现出来了。比如,当Bean A实例化之后,Spring会把它放到三级缓存中,或者可能是一个ObjectFactory。当Bean B需要注入A的时候,Spring会通过这个ObjectFactory来获取A的早期引用,即使A还没有完成属性填充和初始化。这样,B就可以继续创建,而当B创建完成后,A再继续完成它的属性设置和初始化。 不过,我可能混淆了三级缓存的名称和具体存储的内容。正确的三级缓存应该是: 1. 一级缓存:singletonObjects,存放完全初始化好的Bean。 2. 二级缓存:earlySingletonObjects,存放早期暴露的Bean,也就是已经实例化但还没设置属性和初始化的Bean。 3. 三级缓存:singletonFactories,存放Bean工厂,用于生成早期Bean的引用。 当创建Bean A时,首先实例化A,然后将其工厂对象放入三级缓存。接着填充属性时发现需要B,于是去创建B。B在填充属性时又需要A,这时会从三级缓存中找到A的工厂,通过工厂获取早期对象,并将这个早期对象放到二级缓存,同时移除三级缓存中的工厂。这样B就能完成初始化,放入一级缓存。然后A继续填充属性B,完成初始化,之后A也被放入一级缓存。 这样,循环依赖就被解决了。不过需要确定的是,这种机制是否只适用于单例Bean,并且是否只能解决属性注入的循环依赖,而不能解决构造器注入的循环依赖。因为构造器注入的情况下,Bean在实例化的时候就需要依赖的Bean已经准备好,这时候如果存在循环依赖,三级缓存可能无法处理,导致BeanCurrentlyInCreationException。 总结一下,Spring的三级缓存通过提前暴露Bean的早期引用,使得相互依赖的Bean能够在未完全初始化的情况下被引用,从而打破循环。但要注意,这种方式适用于单例作用域的Bean,并且主要解决通过setter或字段注入的循环依赖,构造器注入的循环依赖无法通过这种方式解决,需要调整代码结构或者使用其他方法。</think>Spring通过三级缓存机制解决单例Bean的循环依赖题。以下是逐步解释: --- ### **1. 三级缓存结构** - **一级缓存**:`singletonObjects` **存储完全初始化好的Bean**,可直接使用。 - **二级缓存**:`earlySingletonObjects` **存储早期暴露的Bean**(已实例化但未完成属性填充和初始化)。 - **三级缓存**:`singletonFactories` **存储Bean工厂(ObjectFactory)**,用于生成Bean的早期引用。 --- ### **2. 解决流程(以A←→B依赖为例)** #### **步骤1:创建Bean A** - **实例化A**:调用构造函数创建A对象(此时A未填充属性)。 - **暴露早期引用**:将A的工厂(`ObjectFactory`)存入三级缓存(`singletonFactories`)。 #### **步骤2:填充A的属性** - 发现A依赖B,尝试从容器获取B。 - 容器中没有B,开始创建B。 #### **步骤3:创建Bean B** - **实例化B**:调用构造函数创建B对象。 - **暴露早期引用**:将B的工厂存入三级缓存。 #### **步骤4:填充B的属性** - 发现B依赖A,尝试从容器获取A: 1. **查一级缓存**:无A。 2. **查二级缓存**:无A。 3. **查三级缓存**:找到A的工厂,调用`getObject()`生成A的早期引用。 4. **将A的早期引用存入二级缓存**,并删除三级缓存中的A工厂。 #### **步骤5:完成B的初始化** - B拿到A的早期引用,完成属性填充和初始化。 - **将B存入一级缓存**(`singletonObjects`)。 #### **步骤6:完成A的初始化** - A拿到B的完整实例,继续完成属性填充和初始化。 - **将A存入一级缓存**,并删除二级缓存中的A早期引用。 --- ### **3. 关键点** - **提前暴露引用**:通过三级缓存提前暴露未初始化的Bean,打破循环。 - **仅支持单例Bean**:原型(Prototype)作用域的Bean无法解决循环依赖。 - **构造器注入不适用**:若循环依赖通过构造器注入,实例化阶段就会失败(无法提前暴露引用)。 --- ### **4. 解决不了的场景** - **构造器循环依赖**: ```java // 会抛出BeanCurrentlyInCreationException @Component public class A { private B b; public A(B b) { this.b = b; } } @Component public class B { private A a; public B(A a) { this.a = a; } } ``` - **非单例Bean的循环依赖**:原型Bean每次都会新建实例,无法通过缓存解决。 --- ### **总结** Spring通过三级缓存提前暴露Bean的早期引用,允许相互依赖的Bean在未完全初始化时引用对方,从而解决单例Bean的循环依赖。核心思想是 **“先暴露引用,后完善对象”**。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值