Skip to content

spring 怎么实现 bean 单例?

约 575 字大约 2 分钟

多线程与并发阿里

2025-4-7

⭐ 题目日期:

阿里 - 2024/8/21

📝 题解:

Spring 框架通过以下步骤和机制实现 Bean 的单例模式:

  1. 单例注册表(Singleton Registry)
    Spring 使用 DefaultSingletonBeanRegistry 类中的 singletonObjects(ConcurrentHashMap)存储单例 Bean 的实例。键为 Bean 名称,值为实例对象。此 Map 确保每个 Bean 名称对应唯一实例。

  2. Bean 实例化流程

    • 检查缓存:调用 getBean() 时,首先检查 singletonObjects 中是否存在实例。存在则直接返回。
    • 创建实例:若不存在,同步(synchronized)创建 Bean,防止多线程重复创建。
    • 提前暴露引用:通过 addSingletonFactory 将未完全初始化的 Bean 存入三级缓存,解决循环依赖。
  3. 解决循环依赖(三级缓存)

    • 一级缓存(singletonObjects):存储完全初始化的单例 Bean。
    • 二级缓存(earlySingletonObjects):存储早期暴露的 Bean(已实例化但未填充属性)。
    • 三级缓存(singletonFactories):存储 ObjectFactory,用于生成早期引用。
    • 流程:A 依赖 B,B 依赖 A。创建 A 时,通过 ObjectFactory 提前暴露 A 的引用,B 创建时注入 A 的代理,完成后再初始化 A。
  4. 线程安全机制

    • 同步锁:在 getSingleton() 方法中使用 synchronized 块,确保单例创建阶段的线程安全。
    • 原子性操作:利用 ConcurrentHashMap 的线程安全特性,保证缓存的原子读写。
  5. 生命周期管理

    • 初始化:默认在容器启动时实例化非延迟(non-lazy)单例 Bean。延迟初始化(lazy-init)的 Bean 在首次请求时创建。
    • 销毁:容器关闭时调用单例 Bean 的 destroy 方法,通过 DisposableBean 接口或 @PreDestroy 注解实现。
  6. 示例代码(简化流程)

    public class DefaultSingletonBeanRegistry {
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
        
        public Object getSingleton(String beanName) {
            Object bean = singletonObjects.get(beanName);
            if (bean == null) {
                synchronized (this) {
                    bean = singletonObjects.get(beanName);
                    if (bean == null) {
                        bean = createBean(beanName);
                        singletonObjects.put(beanName, bean);
                    }
                }
            }
            return bean;
        }
    }

总结
Spring 通过单例注册表、三级缓存解决循环依赖、同步锁保障线程安全,以及精准的生命周期管理,确保每个 Bean 定义在容器中仅有一个实例。开发者无需手动实现单例,只需配置作用域为 singleton(默认),Spring 容器自动管理其唯一性及依赖注入。