目录
三级缓存核心原理
循环依赖的解决过程
1. Bean A创建过程中提前曝光工厂
2. Bean B创建时发现依赖A,从缓存获取
3. Bean A继续完成初始化
三级缓存的作用总结
二级缓存为何不够解决缓存依赖?
三级缓存如何解决?
为什么不直接在实例化后创建代理?
总结
Spring通过三级缓存机制解决循环依赖问题,这与populateBean()
方法密切相关。下面我用简化代码解释这个机制:
三级缓存核心原理
Spring在DefaultSingletonBeanRegistry
中维护了三个重要的缓存:
// 一级缓存:存储完全初始化的单例bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 二级缓存:存储早期曝光的单例bean(未完全初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);// 三级缓存:存储单例工厂对象,用于创建早期曝光的bean
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
循环依赖的解决过程
当发生循环依赖时,Spring通过以下步骤解决:
1. Bean A创建过程中提前曝光工厂
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 1. 实例化Bean(调用构造函数)BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);// 2. 提前曝光一个ObjectFactory,用于解决循环依赖boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences);if (earlySingletonExposure) {addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// 3. 填充属性(可能触发对Bean B的依赖)populateBean(beanName, mbd, instanceWrapper);// 4. 初始化Bean(调用init方法和AOP代理)exposedObject = initializeBean(beanName, exposedObject, mbd);return exposedObject;
}// 将工厂添加到三级缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);}}
}
2. Bean B创建时发现依赖A,从缓存获取
当populateBean()
处理B的依赖时:
protected Object resolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {// 查找依赖的bean(假设这里是A)String dependencyName = descriptor.getDependencyName();// 从三级缓存中获取beanObject bean = getSingleton(dependencyName, false);return bean;
}// 从三级缓存中获取bean的核心方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 1. 先从一级缓存获取Object singletonObject = this.singletonObjects.get(beanName);// 2. 如果一级缓存没有且bean正在创建中if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 3. 从二级缓存获取singletonObject = this.earlySingletonObjects.get(beanName);// 4. 二级缓存没有且允许早期引用if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// 5. 从三级缓存获取工厂ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 6. 通过工厂创建早期bean实例singletonObject = singletonFactory.getObject();// 7. 放入二级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 8. 从三级缓存移除this.singletonFactories.remove(beanName);}}}}return singletonObject;
}
3. Bean A继续完成初始化
当B完成创建后,A继续完成自己的初始化:
// 完成Bean A的初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);// 将完全初始化的bean放入一级缓存
addSingleton(beanName, exposedObject);protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);}
}
三级缓存的作用总结
- 一级缓存(singletonObjects):存储完全初始化的单例bean,可直接使用
- 二级缓存(earlySingletonObjects):存储早期曝光的bean(未完成初始化),用于解决循环依赖
- 三级缓存(singletonFactories):存储工厂对象,允许在需要时创建早期bean,并支持AOP代理
通过这种机制,Spring允许在bean尚未完全初始化时就将其引用暴露给依赖它的其他bean,从而解决循环依赖问题。注意:只有单例作用域的bean支持循环依赖,原型模式不支持。你的问题非常关键!实际上,二级缓存理论上可以解决循环依赖,但Spring依然使用三级缓存,主要出于AOP代理的考虑。我来详细解释:
二级缓存为何不够解决缓存依赖?
假设只有两级缓存:
singletonObjects
(完全初始化的bean)earlySingletonObjects
(早期曝光的bean)
当出现循环依赖时,我们可以在实例化后直接将bean放入earlySingletonObjects
。但这样会有一个问题:如果bean需要AOP代理,早期曝光的将是原始对象,而不是代理对象。
举个例子:
// Bean A依赖B,Bean B依赖A
class A {@Autowired private B b;@Transactional public void doSomething() {} // 需要AOP代理
}class B {@Autowired private A a;
}
当A和B循环依赖时:
- A实例化后放入二级缓存(此时是原始对象)
- B创建时从二级缓存获取A的原始对象
- B完成创建,注入A的原始对象
- A继续初始化,创建代理对象
最终结果:B持有的是A的原始对象,而不是代理对象,导致AOP失效!
三级缓存如何解决?
Spring通过三级缓存引入了ObjectFactory
,将AOP代理的创建延迟到真正需要早期引用时:
// 三级缓存:存储工厂对象,用于创建可能需要代理的早期bean
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);// 添加工厂到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));// 获取早期引用的方法(可能创建代理)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;// 如果需要代理,这里会创建代理对象exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;
}
当B需要A的早期引用时:
- 从三级缓存获取
ObjectFactory
- 通过工厂调用
getEarlyBeanReference()
,此时才决定是否创建代理 - 将结果(可能是代理对象)放入二级缓存
这样一来,B持有的就是A的代理对象,保证了AOP的正确性。
为什么不直接在实例化后创建代理?
你可能会问:为什么不直接在实例化A后就创建代理,然后放入二级缓存?这样不就不需要三级缓存了吗?
原因有两点:
- 性能优化:不是所有bean都需要代理,延迟到真正需要时再创建可以避免不必要的代理
- 顺序正确性:Spring的后置处理器执行顺序是有规范的。
postProcessBeforeInitialization
和postProcessAfterInitialization
应该在bean初始化阶段执行,而不是实例化阶段。如果提前创建代理,会破坏这个顺序。
总结
二级缓存可以解决普通对象的循环依赖,但无法解决代理对象的循环依赖。三级缓存通过引入ObjectFactory
,将代理创建延迟到真正需要早期引用时,既保证了AOP的正确性,又维持了后置处理器的执行顺序。这是Spring在循环依赖和AOP之间找到的精妙平衡点。