简介
说明
本文介绍Spring(SpringBoot)为什么使用三级缓存来解决循环依赖(为什么不使用二级)。
问题引出
在上边的分析中我们可以提出两个问题:
- 二级缓存好像没有用到?那么它什么时候会用到?
- 为什么第三级缓存要用一个工厂,删除第三级缓存,只用第一二级不可以吗?
-
可以去掉第二级缓存吗?
简介
不可以去掉第二级缓存。
详解
假如有这种情况:a实例同时依赖于b和c,b和c都依赖于a。
a实例化时,先提前暴露objectFactorya到三级缓存,调用getBean(b)依赖注入b实例。b实例化之后,提前暴露objectFactoryb到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例, b实例完成了依赖注入,升级为一级缓存。a实例化再getBean(c)依赖注入c实例,c实例化之后,提前暴露objectFactoryc到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例。注意这里又要从三级缓存中获取a实例,我们知道三级缓存中的实例是通过调用singletonFactory.getObject()方法获取的,返回结果每次都可能不一样。如果不用二级缓存,这里会有问题,两次获取的a实例不一样。
可以去掉第三级缓存吗?
概述
结论
不可以去掉第三级缓存。
原因
Spring 的设计原则是在 IOC 结束之后再AOP( bean 实例化、属性设置、初始化之后再通过进行AOP(生成代理对象))。即:AOP的实现需要与bean的生命周期的创建分离。
- 为了解决循环依赖但又尽量不打破这个设计原则的情况下,使用了第三级缓存(key:bean名字,value:ObjectFactory)。
- 前边分析过,它是将一个函数式接口作为ObjectFactory,相当于延迟初始化。AOP中发生循环依赖时,通过调用Object的getObject()方法获取到三级缓存中的对象。
- 如果去掉第三级缓存,将AOP的代理工作放到第二级缓存,这样的话,bean在创建过程中就先生成代理对象再初始化和其他工作,与Spring的AOP的设计原则相违背。
详解
上边:“bean何时被加入第3级缓存?” 我们可以得到,第三级缓存里存放的value是:AbstractAutowireCapableBeanFactory#getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object 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;
}
可以看到实际调用SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference(Object bean, String beanName)方法,此接口有以下实现类:
InstantiationAwareBeanPostProcessorAdapter
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
直接返回bean,这里看起来第三级缓存没必要存在。但是,看另一个实现类:
AbstractAutoProxyCreator
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
这里把对象放入第二级缓存。
曾分析过AOP:将横切逻辑织入目标Bean:AbstractAutoProxyCreator#postProcessAfterInitialization方法,在Bean实例化之后被调用。
来看postProcessAfterInitialization
public Object postProcessAfterInitialization( 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;
}
把key对应的项从二级缓存中移除,如果原来二级缓存中就是这个bean,则返回此bean,否则,返回代理类。