结论
解决有代理对象的循环依赖不一定要三级缓存,用二级甚至一级也能解决,下面讨论下Spring为什么选择三级缓存这个方案。
Spring最开始是没有三级缓存的,后面版本因为引入了AOP,有了代理对象,又因为存在循环依赖,为了保证依赖注入过程注入的是代理对象,且不完全打破Spring的设计原则(代理等这些后置处理器应当在初始化阶段完成),Spring选择稍微打破限制,引入三级缓存,提前对循环依赖的bean在依赖注入的时候就生成代理对象。
解释
Spring 现在的三级缓存如下:
/** 第一级缓存,存放可用的成品Bean。 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 第三级缓存,存的是Bean工厂对象 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 第二级缓存,存放半成品的Bean,半成品的Bean是已创建了对象,但是未注入属性和进行初始化*/
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
Spring目前是通过二三级缓存配合,进行循环依赖的解决。
首先,我们要明确Spring遵守的单例bean的创建流程,bean 先实例化,再属性赋值,依赖注入,再初始化。。。。这个基本过程。我们所说的代理对象其实就是初始化阶段,BeanPostProcessor后置处理器完成代理对象的。
网上有很多地方说三级缓存是为了解决代理对象,这个说法并没有说到根本。
如果我们单纯为了解决有代理的循环依赖,其实解决循环依赖用二级缓存甚至一级缓存就可以了,之所以用了三级缓存是Spring开发者的一种取舍造成的。
对于如何解决带有AOP的循环依赖,有如下两种解决方案:
1、无论这个bean有没有循环依赖,在依赖注入之前,就创建好这个bean的代理对象放入缓存,出现依赖注入的时候,直接从这个缓存拿取代理对象即可。
2、不提前创建代理对象,当只有出现循环依赖的时候,才实时地创建代理对象。
Spring 因为为了不完全违背bean的创建流程的定义(代理应当在属性赋值后的初始化过程中生成代理对象),只能勉为其难的提前进行。所以选择了上述的第二种方案。
接下来,说一下为啥选择第二种方案要用三级缓存。
spring 为了优雅,缓存尽量存储的是单一性质的元素,所以必须有第一级缓存,用来存放可用的成品Bean。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
接下来,假如A 和 B 循环依赖,A 和 C也循环依赖,所以当创建A的bean的时候,避免B和C 拿到不同的代理对象,因此我们需要第二个缓存来存储B拿到的A的代理对象,当C去A代理对象的时候,就可以直接从第二个缓存中拿取了。
为了实现只有出现循环依赖的时候才实时地创建代理对象这个过程,Spring 又引入了第三个缓存,第三个缓存的作用是当A在实例化的时候,就把自己放入第三个缓存,代码如下:
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
,表示A正在创建当中,其中() -> getEarlyBeanReference(beanName, mbd, bean))的函数式接口就是用来实现创建代理对象的,
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
当B需要注入A的时候就会执行如下步骤:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
先去第一级缓存拿,没有就去第二级缓存拿,二级没有的话,就去第三级缓存看看,当在第三级缓存发现有A的时候,说明此时A正在创建中,且未被其他bean引用,此时就会从三级缓存中取出beanFactory,beanFactory再执行getObject方法,getObjetc方法就前面的
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
其中SmartInstantiationAwareBeanPostProcessor 是spring内部对于BeanPostProcessor的实现,大家可以自己点去看看,SmartInstantiationAwareBeanPostProcessor 的getEarlyBeanReference就是创建代理对象,并标记自己已经创建了代理对象(earlyProxyReferences)。
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
此时B相当于从三级缓存中拿到了A的代理对象,B为了后面的C和自己拿到的是同一个A的代理对象,他就需要把这个A代理对象放入第二级缓存。同时移除第三级缓存的A,表示A已经提前创建好了代理对象,不需要再从三级缓存里面获取新代理对象了。
接下来,B的创建好后,A继续注入C,C直接从第二级拿到已经创建好了的A的代理对象。A在后面的初始化阶段执行applyBeanPostProcessorsAfterInitialization
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//判断是否提前创建了代理对象
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
的时候,会判断之前是否提前创建了代理对象,这样就解决了带有AOP的循环依赖。
当然,如果A没有循环依赖,那么就不会被其他bean从第三级缓存中取出来执行getEarlyBeanReference方法,这样A的AOP自然就留在了初始化阶段完成了,这样也就遵守了Spring定义的bean的创建过程。
继续解释
继续解释下为什么我说只用二级或者一级缓存也能解决带有AOP的循环依赖问题。
假如不遵守Spring强烈要求bean的创建过程,我们可以直接在依赖注入前,就往第二级缓存存入A的代理对象(如果没有代理就直接存原始对象),这样B和C直接就可以从第二级缓存拿到A的代理对象,这样两个缓存就能解决了,但是这样做就是提前把代理对象都创建好了。文章来源:https://www.toymoban.com/news/detail-858957.html
如果我们更过分点,不遵守每一级缓存存入的是同一过程性质的bean,那么我们只需一级缓存,每个bean提前创建好代理对象就放入一级缓存,(此时一级缓存的bean还是未初始化的bean),接下来B直接从一级拿到A的代理对象,完成创建,B把自己完整的Bean也放入一级缓存,此时一级缓存的bean 就有中间态和完成态两种形态的bean, 最终A完成创建,一级缓存全是完成态Bean。这样做,只用一级缓存就能完成所有的过程,只是不优雅~~。文章来源地址https://www.toymoban.com/news/detail-858957.html
到了这里,关于Spring 为什么要用三级缓存来解决循环依赖(AOP),二级缓存不行吗的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!