跳过正文
  1. 2026s/

spring三级缓存介绍

6 分钟·
x
作者
x
熟练掌握Spring Boot、Spring Cloud等Java技术栈,专注于分布式系统设计与微服务架构。热爱技术分享,探索编程之美。

Spring的三级缓存介绍

Spring框架在处理单例Bean的创建和依赖注入时,使用了三级缓存机制来管理Bean的生命周期和解决潜在的循环依赖问题。这些缓存主要定义在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry类中。下面结合源码进行详细介绍。

  1. 三级缓存的定义和作用

Spring的三级缓存是三个Map结构,用于存储不同阶段的Bean实例或工厂对象。它们分别是:

  • 一级缓存(singletonObjects):存储完全初始化的单例Bean实例。这是最终的缓存,用于存放已经完成实例化、属性注入和初始化后的Bean。

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

作用:提供给外部直接获取的成熟Bean。一旦Bean放入这里,后续的getBean调用就会直接返回它。

  • 二级缓存(earlySingletonObjects):存储早期的Bean实例,即已经实例化但尚未完成属性注入和初始化的Bean引用。主要用于临时存储从三级缓存生成的早期对象,避免重复创建。

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

作用:在循环依赖场景中,提供一个半成品Bean引用给依赖方使用。

  • 三级缓存(singletonFactories):存储ObjectFactory对象,这些工厂用于生成早期的Bean引用(可能包括代理对象)。

/** Creation-time registry of singleton factories: bean name to ObjectFactory. */
private final Map<String, Object> singletonFactories = new ConcurrentHashMap<>(16);

作用:允许在Bean创建过程中提前暴露一个工厂,当发生循环依赖时,通过调用工厂的getObject()方法生成早期Bean引用,从而打破循环。

这些缓存的设计确保了Bean创建的线程安全性和一致性,通过synchronized锁(在代码中体现为singletonLock)来控制并发访问。raw.githubusercontent.com

  1. 三级缓存的工作流程(结合getSingleton方法)

Spring在获取或创建Bean时,主要通过DefaultSingletonBeanRegistry的getSingleton方法来操作这些缓存。getSingleton有多个重载版本,这里重点分析两个关键实现:

  • getSingleton(String beanName, boolean allowEarlyReference):用于获取Bean,支持早期引用。 这个方法首先尝试从一级缓存中获取,如果Bean正在创建中(通过isSingletonCurrentlyInCreation(beanName)判断),则依次检查二级和三级缓存。

protected @Nullable Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock.
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) { // 使用锁确保线程安全
// Consistent creation of early reference within full singleton lock.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject(); // 调用工厂生成早期Bean
                            this.earlySingletonObjects.put(beanName, singletonObject); // 放入二级缓存
                            this.singletonFactories.remove(beanName); // 移除三级工厂
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

流程:一级 → (如果在创建中) 二级 → (如果允许早期引用) 加锁后从三级工厂生成并移到二级。

  • getSingleton(String beanName, ObjectFactory singletonFactory):用于创建Bean,如果不存在则调用提供的factory创建。 这个方法在创建过程中会调用singletonFactory.getObject()来实际实例化Bean,创建完成后调用addSingleton(beanName, singletonObject)将Bean放入一级缓存,并清理二级和三级缓存。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
// ... (检查销毁、beforeSingletonCreation等)
            try {
                singletonObject = singletonFactory.getObject(); // 执行创建
                newSingleton = true;
            }
// ... (异常处理)
            finally {
// ... (afterSingletonCreation)
            }
            if (newSingleton) {
                addSingleton(beanName, singletonObject); // 放入一级,清理二级三级
            }
        }
        return singletonObject;
    }
}

addSingleton方法:


protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

这些方法确保了缓存的层级流动:从三级(工厂)生成早期对象到二级(临时存储),最终到一级(成熟对象)。

  1. 三级缓存如何解决循环依赖(结合doCreateBean方法)

循环依赖是指两个或多个Bean相互依赖,例如A依赖B,B依赖A。在Spring中,这种问题通过三级缓存和早期暴露机制来解决。主要逻辑在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory的doCreateBean方法中实现。

  • 早期暴露的时机:在Bean实例化后(createBeanInstance)、但属性注入(populateBean)前,如果是单例且允许循环引用(allowCircularReferences默认为true)且正在创建中,则添加三级工厂。
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
if(earlySingletonExposure){
        if(logger.

isTraceEnabled()){
        logger.

trace("Eagerly caching bean '"+beanName +
        "' to allow for resolving potential circular references");
    }

addSingletonFactory(beanName, () ->

getEarlyBeanReference(beanName, mbd, bean));  // 添加三级工厂
        }

这里addSingletonFactory将一个Lambda工厂放入singletonFactories,工厂调用getEarlyBeanReference生成早期引用(可能应用AOP代理)。

  • getEarlyBeanReference的实现:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);  // 可能创建代理
        }
    }
    return exposedObject;
}
  • 解决循环依赖的过程: 假设A依赖B,B依赖A: 创建A:实例化A后,添加A的工厂到三级缓存,然后populate A(注入B)。 注入B时,发现B未创建,创建B:实例化B后,添加B的工厂到三级缓存,然后populate B(注入A)。 注入A时,发现A正在创建中(isSingletonCurrentlyInCreation=true),于是从缓存获取A:一级无 → 二级无 → 从三级工厂获取A的早期引用(半成品),放入二级缓存。 B完成populate,使用A的早期引用完成初始化,放入一级缓存。 返回到A,继续populate,使用完整的B完成初始化,放入一级缓存(同时清理二级三级)。

    如果不允许循环引用(allowCircularReferences=false),则不会添加工厂,检测到循环时抛出BeanCurrentlyInCreationException。

    注意:三级缓存是必需的,因为二级只能存储裸Bean,而三级工厂允许在生成早期引用时应用后处理器(如AOP代理),确保一致性。如果只用二级,代理Bean的循环依赖会失败。raw.githubusercontent.com

  1. 是否使用三级缓存解决了循环依赖的问题?

是的,Spring正是通过三级缓存机制解决了单例Bean的循环依赖问题。这种设计允许在Bean创建过程中提前暴露引用,避免无限递归,同时支持AOP等扩展。需要注意的是:

  • 只适用于单例作用域(prototype不支持循环依赖)。
  • 如果涉及构造函数注入的循环依赖,无法解决(因为实例化前无法暴露)。
  • 可以手动关闭循环依赖支持(setAllowCircularReferences(false)),但默认开启。

通过以上源码分析,可以看到三级缓存是Spring IoC容器核心的一部分,确保了依赖注入的可靠性和灵活性。

通过邮件回复

相关文章

Java GC进化路程

4 分钟
1. 概述 # 本博客中我们将展示不同JVM垃圾回收(GC)实现的基本原理。然后我们将学习如何在应用程序中启动特定类型的垃圾回收。

部署基于artalk的评论系统

1 分钟
部署基于artalk的评论系统 # 在现代博客和网站中,评论系统是与读者互动的重要工具。Artalk 是一个开源的评论系统,具有轻量级、易于集成和高度可定制的特点。本文将介绍如何在 Hugo 博客中部署基于 Artalk 的评论系统。

反转链表

反转链表 # 这是我写的算法可以用来参考,上边有测试用例可以用来检测代码写的对不对。