1、三级缓存
1.1、一级缓存
一级缓存:
singletonObjects
功能:存储已完全初始化完成的单例 Bean。
作用:
解决完全初始化后的 Bean 的复用问题:
一级缓存是最终的 Bean 存储地,用于存储所有已完成初始化的单例 Bean。
任何时候需要一个单例 Bean,都会优先从一级缓存中获取,避免重复创建。
提高性能:
如果 Bean 已经完成初始化,直接从一级缓存中返回,性能最高。
局限性:
无法解决循环依赖问题,因为 Bean 必须完全初始化后才能放入一级缓存。如果在 A 的创建过程中需要依赖 B,但 B 尚未完成初始化,就无法通过一级缓存获取。
1.2、二级缓存
二级缓存:
earlySingletonObjects
功能:存储已经实例化但尚未完成初始化的 Bean(半成品)。
作用:
解决循环依赖中 Bean 实例提前引用的问题:
如果 Bean A 正在创建过程中(未完成初始化),但 Bean B 依赖 Bean A,可以从二级缓存中获取 Bean A 的早期引用,避免重复创建 Bean A。
减少三级缓存(代理创建)的性能开销:
二级缓存中存储的是 Bean 的实际实例,而不是代理对象。依赖方直接拿到的是一个实际的 Bean(虽然未完全初始化),因此效率比通过三级缓存获取代理更高。
局限性:
如果依赖链中涉及代理对象(如 AOP 或 CGLIB 代理),二级缓存无法直接提供代理对象,仍需要三级缓存介入。
1.3、三级缓存
解决代理对象的循环依赖
三级缓存:
singletonFactories
功能:存储对象工厂(
ObjectFactory
),用于生成代理或提前暴露对象引用。作用:
解决复杂循环依赖问题:
当二级缓存无法提供实际实例(如需要动态代理对象时),三级缓存通过
ObjectFactory
动态生成 Bean 的早期引用(可以是代理对象)。将代理对象提前暴露,供依赖方使用,避免循环依赖导致的死锁。
提供灵活性:
三级缓存可以生成动态代理对象(如 AOP 增强的 Bean)。
支持更复杂的场景,比如对依赖 Bean 的某些初始化行为进行自定义。
局限性:
三级缓存的性能稍低于一级和二级缓存,因为每次需要通过
ObjectFactory
动态创建对象。
三级缓存伪代码
// 示例:Bean 循环依赖伪代码
public class BeanFactory {
private final Map<String, Object> singletonObjects = new HashMap<>(); // 一级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(); // 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(); // 三级缓存
public Object getBean(String name) {
// 检查一级缓存
Object bean = singletonObjects.get(name);
if (bean == null) {
// 检查二级缓存
bean = earlySingletonObjects.get(name);
if (bean == null) {
// 检查三级缓存,提前暴露对象引用
ObjectFactory<?> factory = singletonFactories.get(name);
if (factory != null) {
bean = factory.getObject();
earlySingletonObjects.put(name, bean);
}
}
}
return bean;
}
public void registerBean(String name, Object bean) {
singletonObjects.put(name, bean);
earlySingletonObjects.remove(name);
singletonFactories.remove(name);
}
}
2、懒加载
@Component
public class A {
@Autowired
@Lazy
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
循环依赖:
A
依赖B
,并且B
又依赖A
。- 如果
@Lazy
不存在,Spring 会在实例化A
时尝试立即注入B
,但此时B
的构造需要A
,形成死锁。通过
@Lazy
解决:
- 使用
@Lazy
将B
的初始化推迟到真正需要时再进行。- 当 Spring 初始化
A
时,先跳过对B
的完整实例化,转而提供一个B
的代理对象。B
的真正初始化在其方法调用时触发,此时A
已经完成初始化,不会再引发循环依赖问题。
3、三级缓存和懒加载的区别
标签:初始化,八股,缓存,JAVA,name,bean,----,Bean,依赖 From: https://blog.csdn.net/weixin_48968553/article/details/143992992