spring的三级缓存,以及循环依赖的形成和解决(详细)

简介: spring的三级缓存,以及循环依赖的形成和解决(详细)


Spring循环依赖

当两个或更多个Bean之间相互依赖时,就会出现Spring循环依赖的问题。这意味着,每个Bean都需要其他Bean才能被创建,而其他Bean又需要该Bean才能被创建。这种情况下,Spring IoC容器会抛出一个异常,告诉你存在循环依赖。

什么是循环依赖

循环依赖是指两个或多个Bean相互依赖,导致Spring IoC容器无法正确创建它们。通常,循环依赖会导致无限递归,最终导致堆栈溢出。下面是一个简单的示例:

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

在这个示例中,A和B相互依赖,因为A需要B才能被创建,而B又需要A才能被创建。这种情况下,Spring IoC容器会抛出一个异常,告诉你存在循环依赖。

spring的三级缓存

什么是三级缓存,是哪三级?

  • singletonObjects:用于存放单例bean实例的缓存,即作用域为singleton的bean。当创建完一个bean后,Spring会将其放入singletonObjects缓存中。在后续获取该bean时,Spring会直接从该缓存中获取,不再需要重新创建。
  • earlySingletonObjects:用于存放创建中的bean实例的缓存。在创建一个bean的过程中,如果需要引用到该bean,则会先从earlySingletonObjects缓存中获取。如果该缓存中不存在该bean的实例,则会通过实例化工厂创建一个新的bean实例。
  • singletonFactories:用于存放创建中的bean实例的工厂缓存。在创建一个bean的过程中,如果需要引用到该bean,则会先从singletonFactories缓存中获取。如果该缓存中不存在该bean的工厂实例,则会通过实例化工厂创建一个新的bean工厂实例。在后续获取该bean时,Spring会从该缓存中获取bean工厂实例,并通过该工厂创建新的bean实例。

在以上三个缓存中,earlySingletonObjects缓存和singletonFactories缓存的作用是相同的,即存放创建中的bean实例。它们的区别在于earlySingletonObjects缓存存放的是已经创建但还没有完全初始化的bean实例,而singletonFactories缓存存放的是bean实例工厂

缓存的初始化

当我们获取一个bean时,Spring首先会从singletonObjects缓存中查找是否存在该bean的实例。如果存在,则直接返回该实例;否则,Spring会检查该bean是否在earlySingletonObjects缓存中。如果在,Spring会等待该bean完全初始化后再返回该bean实例;否则,Spring会检查singletonFactories缓存中是否存在该bean实例的工厂。如果存在,Spring会通过该工厂创建一个新的bean实例,并将其放入earlySingletonObjects缓存中,等待其完全初始化后再放入singletonObjects缓存中

缓存的清理

Spring的缓存机制采用的是弱引用的方式,因此当一个bean实例不再被引用时,会被垃圾回收机制回收。同时,Spring还提供了一些缓存清理的方法,以便在需要时手动清理缓存。具体而言,Spring提供了以下几种清理缓存的方法:

  • destroySingletons():销毁所有singleton作用域的bean实例,并清空相关的缓存。
  • clearSingletonCache():清空singletonObjects、earlySingletonObjects和singletonFactories三个缓存。
  • removeSingleton(String beanName):从缓存中移除指定bean

循环依赖的解决

spring三级缓存解决循环依赖

在 Spring 中,当出现循环依赖时,通过三级缓存解决。Spring 使用三级缓存来存储正在创建的 bean,以便在创建 bean 的过程中检测循环依赖。以下是 Spring 如何使用三级缓存解决循环依赖的步骤:

  1. 在创建 bean 的过程中,Spring 首先会将正在创建的 bean 放入三级缓存中。
  2. 当创建 bean 的过程中需要依赖另一个 bean 时,Spring 会检查三级缓存中是否存在需要依赖的 bean。如果存在,Spring 会返回缓存中的 bean 实例。
  3. 如果三级缓存中不存在需要依赖的 bean,则 Spring 会将需要依赖的 bean 标记为正在创建中,并继续创建该 bean。在创建 bean 的过程中,Spring 会将需要依赖的 bean 放入二级缓存中。
  4. 如果需要依赖的 bean 还依赖当前正在创建的 bean,则 Spring 会检测二级缓存中是否存在需要依赖的 bean。如果存在,则 Spring 会返回缓存中的 bean 实例。
  5. 如果二级缓存中不存在需要依赖的 bean,则 Spring 会将需要依赖的 bean 标记为正在创建中,并继续创建该 bean。在创建 bean 的过程中,Spring 会将需要依赖的 bean 放入一级缓存中。
  6. 在创建 bean 的过程中,如果发现依赖的 bean 依然依赖当前正在创建的 bean,Spring 将抛出 BeanCurrentlyInCreationException 异常,以避免出现死循环依赖。
  7. 当所有依赖项都已创建完毕时,Spring 会从一级缓存中获取 bean 实例,并将其放入二级缓存和三级缓存中。此时,bean 已经可以使用。

总的来说,Spring 的三级缓存是一种很好的机制,可以解决循环依赖问题,但是如果循环依赖过于复杂,还是需要手动解决。

手动解决循环依赖

当程序报错是org.springframework.beans.factory.BeanCurrentlyInCreationException,就需要手动去解决循环依赖了。在Spring中,手动解决循环依赖可以使用以下两种方式:

  1. 使用构造函数注入:将循环依赖的Bean通过构造函数注入,而不是使用属性注入。这样在创建Bean时就能够通过构造函数注入其他Bean,从而避免了循环依赖。
  2. 使用@Lazy注解:在Spring中,@Lazy注解可以将Bean的初始化时机推迟到第一次使用时。这样,如果存在循环依赖,其中一个Bean在初始化时会直接使用另一个Bean的代理对象,从而避免了循环依赖。
    ⚠️需要注意的是,手动解决循环依赖可能会导致代码复杂度提高,并且需要谨慎处理,以避免出现其他问题。因此,在实际开发中,建议尽可能地避免循环依赖,以便减少出现问题的可能性。
相关文章
|
1月前
|
XML 缓存 Java
Spring源码之 Bean 的循环依赖
循环依赖是 Spring 中经典问题之一,那么到底什么是循环依赖?简单说就是对象之间相互引用, 如下图所示: 代码层面上很好理解,在 bean 创建过程中 class A 和 class B 又经历了怎样的过程呢? 可以看出形成了一个闭环,如果想解决这个问题,那么在属性填充时要保证不二次创建 A对象 的步骤,也就是必须保证从容器中能够直接获取到 B。 一、复现循环依赖问题 Spring 中默认允许循环依赖的存在,但在 Spring Boot 2.6.x 版本开始默认禁用了循环依赖 1. 基于xml复现循环依赖 定义实体 Bean java复制代码public class A {
|
1月前
|
存储 缓存 Java
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
|
29天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
67 0
|
9天前
|
存储 缓存 Java
Spring解决循环依赖
Spring解决循环依赖
|
26天前
|
缓存 算法 Java
开发必懂的Spring循环依赖图解 Spring 循环依赖
开发必懂的Spring循环依赖图解 Spring 循环依赖
18 1
|
29天前
|
缓存 算法 Java
Spring解决循环依赖
Spring解决循环依赖
19 0
|
1月前
|
缓存 NoSQL Java
spring cache整合redis实现springboot项目中的缓存功能
spring cache整合redis实现springboot项目中的缓存功能
46 1
|
1月前
|
Java 开发者 Spring
【Java】Spring循环依赖:原因与解决方法
【Java】Spring循环依赖:原因与解决方法
39 0
|
8月前
|
缓存 Java Spring
最通俗的方式理解Spring循环依赖三级缓存
有位粉丝找我,说要耽误我5分钟时间,想让我帮助它理解一下Spring循环依赖的三级缓存,绕晕了一个星期,没有想明白。我想今天,用最通俗易懂的方式给大家重新梳理一下,保证让你听懂了。
81 0
|
缓存 Java Spring
Spring的循环依赖和三级缓存
Spring的循环依赖和三级缓存
806 0
Spring的循环依赖和三级缓存