spring singleton bean 获取采用三级缓存协同机制,非手写dcl:先查singletonobjects(已初始化),再earlysingletonobjects(半成品),最后singletonfactories(工厂),加锁前/后双重检查,concurrenthashmap保障线程安全与可见性。

Spring 容器中 Singleton Bean 的获取,底层确实用到了类似双重检查锁定(DCL)的思想,但不是直接照搬 Java 手写的 DCL 单例模式——它更轻量、更契合容器生命周期,且天然规避了部分手写 DCL 的陷阱。
为什么 Spring 不需要手写 volatile + synchronized 的 DCL
Spring 的单例 Bean 在容器启动时(或首次 getBean 时)完成初始化,并由 DefaultSingletonBeanRegistry 统一管理。核心逻辑在 getSingleton(String beanName, boolean allowEarlyReference) 方法中,其结构体现“双重检查”特征:
-
第一次检查:先从
singletonObjects(已完全初始化的单例缓存)中直接 get,无锁、快速返回 - 若未命中,再查 earlySingletonObjects(早期暴露的半成品对象,用于解决循环依赖),仍无则进入加锁流程
-
第二次检查:在 synchronized 块内,再次从
singletonObjects查——防止其他线程已创建完毕,避免重复初始化
关键差异:Spring 的“锁”和“volatile”由容器保障
手写 DCL 必须用 volatile 防止指令重排序导致“构造未完成就赋值”,而 Spring 不需要:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- Bean 实例化过程(
createBeanInstance→populateProperties→initializeBean)由容器严格串行控制,不会被 JVM 重排序干扰可见性 - 所有单例对象最终都存入
ConcurrentHashMap类型的singletonObjects,该 Map 本身提供内存可见性和线程安全的 put/get - 锁粒度是
singletonObjects对象本身,不是类对象,避免全局竞争
真正起作用的是三级缓存机制
Spring 的单例保障本质靠的是“三级缓存协同”,这比单纯 DCL 更健壮:
-
singletonObjects:存放完全初始化好的单例(最常用) -
earlySingletonObjects:存放提前暴露的原始对象(仅用于解决 A→B→A 循环依赖) -
singletonFactories:存放 ObjectFactory 工厂(延迟创建,避免过早实例化)
每次 getBean 都按“一级→二级→三级”顺序尝试获取,既保证性能(多数请求走一级缓存),又支持复杂场景(如循环依赖),还天然线程安全。
你不需要手动实现,但得理解它的边界
Spring 的单例是“容器级单例”,不是 JVM 级单例:
- 同一个
ApplicationContext中,Bean 名称相同则返回同一实例 - 不同容器(如父子容器、多个 Spring Boot 应用上下文)之间互不影响
- 不适用于跨 JVM 或分布式场景——这时要靠外部协调(如 Redis 分布式锁、配置中心)










