当前位置: 首页 > news >正文

Spring中的三级缓存和循环依赖

三级缓存

在 Spring 中,Bean 的创建过程涉及到三级缓存。这三级缓存分别是 singletonObjects、earlySingletonObjects 和 singletonFactories。它们在 Spring 容器启动时用于存储正在创建的 Bean 实例。

在 Spring 源码中,三级缓存涉及到了 DefaultSingletonBeanRegistry 类。这个类是 Spring 容器中负责管理单例 Bean 的注册表,其中包含了三级缓存的实现。下面是 DefaultSingletonBeanRegistry 中与三级缓存相关的源码片段:

public abstract class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    
    // 一级缓存:singletonObjects
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    // 二级缓存:earlySingletonObjects
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    // 三级缓存:singletonFactories
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    ...

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 从一级缓存中获取 Bean 实例
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 从二级缓存中获取 Bean 实例
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    // 从三级缓存中获取 Bean 实例的 ObjectFactory
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 使用 ObjectFactory 创建 Bean 实例
                        singletonObject = singletonFactory.getObject();
                        // 将创建好的 Bean 实例放入二级缓存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 从三级缓存中移除 ObjectFactory
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

    ...

}

DefaultSingletonBeanRegistry 类中,singletonObjects 用于存储已经完成初始化的 Bean 实例,earlySingletonObjects 用于存储尚未完成初始化的 Bean 实例,singletonFactories 用于存储创建 Bean 的工厂对象。在 getSingleton() 方法中,首先会尝试从一级缓存 singletonObjects 中获取 Bean 实例,如果找不到且 Bean 正在创建中,则会从二级缓存 earlySingletonObjects 中获取 Bean 实例,如果仍然找不到且允许早期引用,则会从三级缓存 singletonFactories 中获取 Bean 实例的工厂对象,并使用工厂对象创建 Bean 实例,然后将创建好的 Bean 实例放入二级缓存 earlySingletonObjects 中。

  1. singletonObjects:

    • 这是最常见的单例对象缓存。当容器创建 Bean 时,会首先尝试从 singletonObjects 缓存中获取已经创建好的 Bean 实例。
    • 如果能够从 singletonObjects 中获取到 Bean 实例,就直接返回该实例。
    • 如果 singletonObjects 缓存中不存在 Bean 实例,则继续后续的创建流程。
  2. earlySingletonObjects:

    • 这个缓存用于存储尚未完成初始化的早期单例对象。
    • 在创建 Bean 的过程中,如果发现 Bean 的初始化依赖其他 Bean,而这些依赖的 Bean 正好是单例的,则会暂时将正在创建的 Bean 实例放入 earlySingletonObjects 缓存中,以便解决循环依赖的问题。
    • 等到 Bean 的创建完成后,会将其移动到 singletonObjects 缓存中。
  3. singletonFactories:

    • 这个缓存用于存储创建 Bean 的工厂对象(ObjectFactory)。
    • 当 Bean 的创建过程中需要解决循环依赖时,会将创建 Bean 的工厂对象放入 singletonFactories 缓存中。
    • 当需要获取正在创建的 Bean 的依赖时,会从 singletonFactories 缓存中获取对应的工厂对象,然后通过工厂对象创建 Bean 的代理对象,并将代理对象放入 earlySingletonObjects 缓存中,以便解决循环依赖的问题。

这三级缓存在 Spring 容器启动时起到了至关重要的作用,它们协同工作以解决 Bean 的循环依赖问题,确保容器能够正确地创建和管理 Bean 实例。


循环依赖

循环依赖是指两个或多个 Bean 之间相互依赖形成的循环引用关系。在 Spring 中,循环依赖通常出现在以下场景中:

  1. 构造器注入循环依赖: 当两个或多个 Bean 在它们的构造器中相互依赖时,就会出现构造器注入的循环依赖。例如,BeanA 的构造器参数依赖于 BeanB,而 BeanB 的构造器参数又依赖于 BeanA。

  2. setter 方法注入循环依赖: 当两个或多个 Bean 在它们的 setter 方法中相互依赖时,就会出现 setter 方法注入的循环依赖。例如,BeanA 的某个属性依赖于 BeanB,而 BeanB 的某个属性又依赖于 BeanA。

为了解决循环依赖的问题,Spring 使用了三级缓存的机制。这个机制的原理如下:

  1. 当容器创建 Bean 的过程中,如果发现循环依赖,会将正在创建的 Bean 实例放入到早期单例对象的缓存中(即二级缓存 earlySingletonObjects)。

  2. 同时,为了解决循环依赖,会创建 Bean 的工厂对象(即 ObjectFactory),并将工厂对象放入到三级缓存 singletonFactories 中。

  3. 当需要解决循环依赖时,容器会从三级缓存 singletonFactories 中获取 Bean 的工厂对象,并使用工厂对象创建 Bean 的代理对象。

  4. 创建好的代理对象会被放入到二级缓存 earlySingletonObjects 中,以便在后续的 Bean 创建过程中能够获取到已经创建的 Bean 的代理对象。

  5. 当所有 Bean 都创建完成后,容器会再次遍历二级缓存 earlySingletonObjects 中的 Bean 实例,将它们初始化并放入一级缓存 singletonObjects 中。

通过三级缓存的机制,Spring 能够解决循环依赖的问题,确保容器能够正确地创建和管理 Bean 实例。这种机制下,即使存在循环依赖,也能够保证所有 Bean 都能够正确地初始化和注入依赖。

相关文章:

  • 玩转盲盒潮流:从0到1搭建小程序平台
  • “高考钉子户”唐尚珺决定再战2024年高考
  • 安装错误提示Please run MaterialLibrary2018.msi first或者其他MaterialLibrary版本
  • PostgreSQL用户与角色简述
  • 键盘盲打是练出来的
  • 状压dp 例题
  • 深入C++:深拷贝VS浅拷贝,编程高手必懂的技巧与陷阱
  • Spring Cloud 之 Gateway
  • 缪尔赛思又来到了你的面前(哈希)
  • 三台泵恒压供水站电控系统及PLC程序设计实例
  • 每日5题Day9 - LeetCode 41 - 45
  • git 查看远程分支地址
  • WordPress 发布了独立的 SQLite 插件
  • 阿里云oss存储直传回调服务的内网穿透
  • static的了解
  • 软件即服务-SaaS
  • 20.有序性与内存屏障
  • 设计模式14——组合模式
  • vue3的api风格
  • CTF-web-WP-攻防世界-1
  • 澎湃读报丨解放日报9个版聚焦:上海,加快建成具有全球影响力的科技创新高地
  • 科学家为AI模型设置“防火墙”,以防止被不法分子滥用
  • 国家发改委下达今年第二批810亿超长期特别国债资金,支持消费品以旧换新
  • 中国体育报关注徐梦桃、王曼昱、盛李豪等获评全国先进工作者:为建设体育强国再立新功
  • 高璞任中国第一汽车集团有限公司党委常委、副总经理
  • 地下管道密布成难题,道路修整如何破局?