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的代理对象,从而避免了循环依赖。
    ⚠️需要注意的是,手动解决循环依赖可能会导致代码复杂度提高,并且需要谨慎处理,以避免出现其他问题。因此,在实际开发中,建议尽可能地避免循环依赖,以便减少出现问题的可能性。
相关文章
|
6天前
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
19天前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
58 2
|
2月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
190 24
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
1月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
31 1
|
2月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
40 4
|
2月前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
39 1
|
3月前
|
缓存 Java Spring
spring如何解决循环依赖
Spring框架处理循环依赖分为构造器循环依赖与setter循环依赖两种情况。构造器循环依赖不可解决,Spring会在检测到此类依赖时抛出`BeanCurrentlyInCreationException`异常。setter循环依赖则通过缓存机制解决:利用三级缓存系统,其中一级缓存`singletonObjects`存放已完成的单例Bean;二级缓存`earlySingletonObjects`存放实例化但未完成属性注入的Bean;三级缓存`singletonFactories`存放创建这些半成品Bean的工厂。
|
3月前
|
缓存 Java Spring
Spring缓存实践指南:从入门到精通的全方位攻略!
【8月更文挑战第31天】在现代Web应用开发中,性能优化至关重要。Spring框架提供的缓存机制可以帮助开发者轻松实现数据缓存,提升应用响应速度并减少服务器负载。通过简单的配置和注解,如`@Cacheable`、`@CachePut`和`@CacheEvict`,可以将缓存功能无缝集成到Spring应用中。例如,在配置文件中启用缓存支持并通过`@Cacheable`注解标记方法即可实现缓存。此外,合理设计缓存策略也很重要,需考虑数据变动频率及缓存大小等因素。总之,Spring缓存机制为提升应用性能提供了一种简便快捷的方式。
50 0
|
3月前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
67 0
|
3月前
|
Java Spring 容器
循环依赖难破解?Spring Boot神秘武器@RequiredArgsConstructor与@Lazy大显神通!
【8月更文挑战第29天】在Spring Boot应用中,循环依赖是一个常见问题。当两个或多个Bean相互依赖形成闭环时,Spring容器会陷入死循环。本文通过对比@RequiredArgsConstructor和@Lazy注解,探讨它们如何解决循环依赖问题。**@RequiredArgsConstructor**:通过Lombok生成包含final字段的构造函数,优先通过构造函数注入依赖,简化代码但可能导致构造函数复杂。**@Lazy**:延迟Bean的初始化,直到首次使用,打破创建顺序依赖,增加灵活性但可能影响性能。根据具体场景选择合适方案可有效解决循环依赖问题。
119 0