spring是通过三级缓存与双重检测锁的协作来保证bean是单例的
一级缓存(singletonObjects):存储完全初始化后的单例 Bean 实例。
二级缓存(earlySingletonObjects):存储提前暴露的 Bean 引用(未完成属性填充和初始化)。
三级缓存(singletonFactories):存储 Bean 工厂对象(ObjectFactory),用于解决循环依赖时提前生成代理对象
关键源码流程(参考 DefaultSingletonBeanRegistry.getSingleton()):
1 | public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { |
延伸问题: 为什么1级缓存是线程安全的容器,而二三级缓存是非线程的容器
首先,1级缓存存储完整单例对象的容器,是全局共享的,所有线程都可能并发访问,必须保证高并发下的数据一致性;所以采用了CHM锁机制,保证线程安全。
1级缓存的创建是为了支持高并发的读取,而不是创建;bean的创建是串行化,并不存在并发创建的场景;
Bean 的创建过程通过同步机制保证线程安全,而 CHM 则优化了初始化后的并发访问性能
其次,二级缓存是存储早期暴露的 Bean 引用(未完成属性注入和初始化),主要用于解决循环依赖问题(如 A 依赖 B,B 又依赖 A)
虽然使用 HashMap,但其访问仅在同步代码块内进行(例如 getSingleton() 方法中的 synchronized 锁),通过锁机制保证线程安全。
另外二级缓存的生命周期较短,仅在 Bean 创建过程中临时使用,且通过同步块控制并发,无需额外线程安全容器的开销
最后,三级缓存存储 ObjectFactory 工厂对象,用于生成 Bean 的早期引用(可能包含代理对象),解决循环依赖和 AOP 代理的动态生成问题
三级缓存的写入和读取操作集中在单一线程的 Bean 创建流程中(如 createBean() 方法),不存在并发场景,因此无需线程安全容器。
工厂对象(ObjectFactory)仅被调用一次(生成早期引用后即移除),生命周期极短,无需复杂同步机制,所以也是普通hashMap