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的代理对象,从而避免了循环依赖。
    ⚠️需要注意的是,手动解决循环依赖可能会导致代码复杂度提高,并且需要谨慎处理,以避免出现其他问题。因此,在实际开发中,建议尽可能地避免循环依赖,以便减少出现问题的可能性。
相关文章
|
2月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
529 5
|
2月前
|
存储 缓存 Java
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
缓存是提升应用性能的重要技术,Spring框架提供了丰富的缓存注解,如`@Cacheable`、`@CacheEvict`等,帮助开发者简化缓存管理。本文介绍了如何在Spring中配置缓存管理器,使用缓存注解优化数据访问,并探讨了缓存的最佳实践,以提升系统响应速度与可扩展性。
310 0
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
|
7月前
|
监控 安全 Java
解决 Spring Boot 中 SecurityConfig 循环依赖问题的详解
本文详细解析了在 Spring Boot 中配置 `SecurityConfig` 时可能遇到的循环依赖问题。通过分析错误日志与代码,指出问题根源在于 `SecurityConfig` 类中不当的依赖注入方式。文章提供了多种解决方案:移除 `configureGlobal` 方法、定义 `DaoAuthenticationProvider` Bean、使用构造函数注入以及分离配置类等。此外,还讨论了 `@Lazy` 注解和允许循环引用的临时手段,并强调重构以避免循环依赖的重要性。通过合理设计 Bean 依赖关系,可确保应用稳定启动并提升代码可维护性。
622 0
|
8月前
|
Java Maven 微服务
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的 maven 依赖
在项目中使用Swagger2工具时,需导入Maven依赖。尽管官方最高版本为2.8.0,但其展示效果不够理想且稳定性欠佳。实际开发中常用2.2.2版本,因其稳定且界面友好。以下是围绕2.2.2版本的Maven依赖配置,包括`springfox-swagger2`和`springfox-swagger-ui`两个模块。
339 0
|
6月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
237 32
|
4月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
426 0
|
8月前
|
缓存 Java 应用服务中间件
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——依赖导入和Thymeleaf相关配置
在Spring Boot中使用Thymeleaf模板,需引入依赖`spring-boot-starter-thymeleaf`,并在HTML页面标签中声明`xmlns:th="http://www.thymeleaf.org"`。此外,Thymeleaf默认开启页面缓存,开发时建议关闭缓存以实时查看更新效果,配置方式为`spring.thymeleaf.cache: false`。这可避免因缓存导致页面未及时刷新的问题。
361 0
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
11月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
273 2
|
11月前
|
缓存 NoSQL Java
Spring Boot中的分布式缓存方案
Spring Boot提供了简便的方式来集成和使用分布式缓存。通过Redis和Memcached等缓存方案,可以显著提升应用的性能和扩展性。合理配置和优化缓存策略,可以有效避免常见的缓存问题,保证系统的稳定性和高效运行。
341 3
下一篇
oss云网关配置