外观
spring 怎么实现 bean 单例?
⭐ 题目日期:
阿里 - 2024/8/21
📝 题解:
Spring 框架通过以下步骤和机制实现 Bean 的单例模式:
单例注册表(Singleton Registry)
Spring 使用DefaultSingletonBeanRegistry
类中的singletonObjects
(ConcurrentHashMap)存储单例 Bean 的实例。键为 Bean 名称,值为实例对象。此 Map 确保每个 Bean 名称对应唯一实例。Bean 实例化流程
- 检查缓存:调用
getBean()
时,首先检查singletonObjects
中是否存在实例。存在则直接返回。 - 创建实例:若不存在,同步(synchronized)创建 Bean,防止多线程重复创建。
- 提前暴露引用:通过
addSingletonFactory
将未完全初始化的 Bean 存入三级缓存,解决循环依赖。
- 检查缓存:调用
解决循环依赖(三级缓存)
- 一级缓存(singletonObjects):存储完全初始化的单例 Bean。
- 二级缓存(earlySingletonObjects):存储早期暴露的 Bean(已实例化但未填充属性)。
- 三级缓存(singletonFactories):存储 ObjectFactory,用于生成早期引用。
- 流程:A 依赖 B,B 依赖 A。创建 A 时,通过 ObjectFactory 提前暴露 A 的引用,B 创建时注入 A 的代理,完成后再初始化 A。
线程安全机制
- 同步锁:在
getSingleton()
方法中使用 synchronized 块,确保单例创建阶段的线程安全。 - 原子性操作:利用 ConcurrentHashMap 的线程安全特性,保证缓存的原子读写。
- 同步锁:在
生命周期管理
- 初始化:默认在容器启动时实例化非延迟(non-lazy)单例 Bean。延迟初始化(lazy-init)的 Bean 在首次请求时创建。
- 销毁:容器关闭时调用单例 Bean 的
destroy
方法,通过DisposableBean
接口或@PreDestroy
注解实现。
示例代码(简化流程)
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 容器自动管理其唯一性及依赖注入。