什么是三级缓存
按照目前我们实现的 Spring 框架,是可以满足一个基本需求的,但如果你配置了A、B两个Bean对象互相依赖,那么立马会抛出 java.lang.StackOverflowError,为什么呢?因为A创建时需要依赖B创建,而B的创建又依赖于A创建,就这样死循环了,就会不断地去实例化对象。
而这个循环依赖基本也可以说是 Spring 中非常经典的实现了,所要解决的场景主要有以下三种情况:
循环依赖分为三种,自身依赖于自身、互相循环依赖、多组循环依赖。但无论循环依赖的数量有多少,循环依赖的本质是一样的。就是你的完整创建依赖于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失败。所以 Spring 提供了除了构造函数注入和原型注入外的,setter循环依赖注入解决方案。
Spring如何处理循环依赖
简介
Spring通过三级缓存和提前曝光机制来解决循环依赖问题,这三级缓存为:
- singletonObject:一级缓存,该缓存key = beanName, value = bean;这里的bean是已经创建完成的,该bean经历过实例化->属性填充->初始化以及各类的后置处理。因此,一旦需要获取bean时,我们第一时间就会寻找一级缓存。
- earlySingletonObjects:二级缓存,该缓存key = beanName, value = bean;这里跟一级缓存的区别在于,该缓存所获取到的bean是提前曝光出来的,是还没创建完成的。也就是说获取到的bean只能确保已经进行了实例化,但是属性填充跟初始化肯定还没有做完,因此该bean还没创建完成,仅仅能作为指针提前曝光,被其他bean所引用。
- singletonFactories:三级缓存,该缓存key = beanName, value = beanFactory;在bean实例化完之后,属性填充以及初始化之前,如果允许提前曝光,spring会将实例化后的bean提前曝光,也就是把该bean转换成beanFactory并加入到三级缓存。在需要引用提前曝光对象时再通过singletonFactory.getObject()获取。
核心源码刨析
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存获取,key=beanName value=bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从二级缓存获取,key=beanName value=bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 是否允许循环引用
if (singletonObject == null && allowEarlyReference) {
/**
* 三级缓存获取,key=beanName value=objectFactory,objectFactory中存储getObject()方法用于获取提前曝光的实例
*
* 而为什么不直接将实例缓存到二级缓存,而要多此一举将实例先封装到objectFactory中?
* 主要关键点在getObject()方法并不一定直接返回实例,而有可能对实例又使用
* SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法对bean进行处理
*
* 也就是说,spring中所有的单例bean在实例化后都会被进行提前曝光到三级缓存中,
* 但是并不是所有的bean都存在循环依赖,也就是说三级缓存到二级缓存的步骤不一定都会被执行,有可能曝光后直接创建完成,没被提前引用过,
* 就直接被加入到一级缓存中。
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
/**
* 通过getObject()方法获取bean,通过此方法获取到的实例不单单是提前曝光出来的实例,
* 它还经过了SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法处理过(使用AOP时进行代理,否则不做任何操作)。
* 这也正是三级缓存存在的意义,可以通过重写该后置处理器对提前曝光的实例,在被提前引用时进行一些操作
*/
singletonObject = singletonFactory.getObject();
// 将三级缓存生产的bean放入二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 删除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// 1
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
final Object bean = instanceWrapper.getWrappedInstance();
if (earlySingletonExposure) {
// 2
addSingletonFactory(beanName, new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// 3
Object exposedObject = bean;
// 4
populateBean(beanName, mbd, instanceWrapper);
// 5
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
if (earlySingletonExposure) {
// 6
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 7
// exposedObject跟bean一样,说明初始化操作没用应用Initialization后置处理器(指AOP操作)改变exposedObject
// 主要是因为exposedObject如果提前代理过(存在AOP的情况下),就会跳过Spring AOP代理,所以exposedObject没被改变,也就等于bean了
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 8
...
}
}
}
return exposedObject;
}
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;
}
// AbstractAutoProxyCreator类
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 如果执行AOP操作则返回代理对象,否则返回原对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
整体流程
上面流程中,做了部分删减。但基本创建一个bean,就这几步了。
1处,创建bean对象,此时,属性什么的全是null,可以理解为,只是new了,field还没设置
2处,添加到第三级缓存;加进去的,只是个factory,只有发生提前引用的时候,才会发挥作用
3处,把原始bean,存到exposedObject
4处,填充属性;循环依赖情况下,A/B循环依赖。假设当前为A,那么此时填充A的属性的时候,会去:
new B;
填充B的field,发现field里有一个是A类型,然后就去getBean(“A”),然后走到第三级缓存,拿到了A的ObjectFactory,然后调用ObjectFactory的getObject,然后调用AOP的后置处理器类的getEarlyBeanReference方法,拿到代理后的bean(假设此处切面满足,要创建代理),并且从三级缓存中删除A的ObjectFactory,然后将代理了不完整A的代理对象放到二级缓存中;
经过上面的步骤后,B里面,field已经填充ok,其中,且填充的field是代理后的A,这里命名为proxy A。
B 继续其他的后续处理,如果需要的话在初始化完成后,B会进行AOP动态代理,创建并返回代理类proxyB。
B处理完成后,被填充到当前的origin A(原始A)的field中(如果满足切面则填充的是proxyB),同时将B或proxyB放到一级缓存中并且删除二三级缓存。
5处,对A进行后置处理,此时调用aop后置处理器的,postProcessAfterInitialization;前面我们说了,此时不会再去调用wrapIfNecessary,所以这里直接返回原始A,即 origin A;
6处,去缓存里获取A,拿到的A是二级缓存中的proxy A;
7处,我们梳理下:
exposedObject:origin A
bean:原始A
earlySingletonReference: proxy A
此时,下面这个条件是满足的,所以,exposedObject,最终被替换为proxy A:
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
至此,循环依赖问题已经被解决掉了!
为什么是三级缓存
二级缓存的必要性
如果只有一级缓存的话,那么根据提前曝光机制需要将半成品对象也放入到缓存中,这样的话缓存里既缓存了完整的Bean也缓存了半成品Bean。如果这时候,有其他线程去这个缓存里获取bean来用怎么办?拿到的bean,不完整,怎么办呢?属性都是null,直接空指针了。所以必须要再加一个缓存,来将完整的Bean和半成品Bean分开。
三级缓存的必要性
如果没有AOP的话,只有二级缓存就够了!但是现在考虑这样一种情况:发生循环依赖的两个Bean都是满足切面的Bean,都需要进行动态代理,则再为A进行属性填充时会去创建B,在为B进行属性填充时又需要引用A的代理类proxyA,但是我们知道AOP动态代理是发生在初始化之后(此时A还停留在属性填充阶段),如果只有二级缓存,在B进行属性填充的时候就没办法等到A初始化完成后生成proxy然后被B的属性引用。所以就有了第三级缓存,用来存放一个工厂对象ObjectFactory,它的作用就是在满足切面的时候将AOP提前,也就是将生成proxyA的过程从初始化完A后提前至B的属性填充的时候,我们通过ObjectFactory#getObject就可以拿到代理对象,这样就解决了发生循环依赖时的AOP问题。
AOP时机:
①:如果没有循环依赖的话,在bean初始化完成后创建动态代理
②:如果有循环依赖,在bean实例化之后创建!
多例和构造器为什么无法解决循环依赖
多例
如果是原型bean,那么就意味着每次创建对象时都不会从缓存中获取,并且每一次都不会将之添加至缓存中,而是去创建新对象。这样的话,在为B进行属性填充时发现依赖A,但是并不会去缓存中找,而是继续去创建,这样依旧还是死循环。
构造器
因为构造器是在实例化时调用的,此时bean还没有实例化完成,如果此时出现了循环依赖,一二三级缓存并没有Bean实例的任何相关信息(在实例化之后才放入三级缓存中),因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖的异常了。
手写简易版Spring系列处理循环依赖问题
循环依赖的核心功能实现主要包括 DefaultSingletonBeanRegistry 提供三级缓存:singletonObjects 、 earlySingletonObjects 、singletonFactories ,分别存放成品对象、半成品对象和工厂对象。同时包
装三个缓存提供方法: getSingleton 、 registerSingleton 、 addSingletonFactory ,这样使用方就可以分别在不同时间段存放和获取对应的对象了。
在 AbstractAutowireCapableBeanFactory 的 doCreateBean 方法中,提供了关于提前暴露对象的操作, addSingletonFactory(beanName, () --> getEarlyBeanReference(beanName, bea nDefinition, finalBean));finalBean));,以及后续获取对象和注册对象的操作 exposedObject = getSingleton(beanName); registerSingleton(beanName, exposedObject);exposedObject);,经过这样的处理就可以完成对复杂场景循环依赖的操作。
另外在 DefaultAdvisorAutoProxyCreator 提供的切面服务中,也需要实现接口InstantiationAwareBeanPostProcessor 新增的 getEarlyBea nReference 方法,便于把依赖的切面对象也能存放到三级缓存中,处理对应的循环依赖。
设置三级缓存
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
// 一级缓存,用来保存单例对象的实例(完整对象)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存,用来缓存没有完成属性填充等操作的半成品对象
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 保存实现了销毁方法的Bean实例
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
// 用来代表null
protected static final Object NULL_OBJECT = new Object();
@Override
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = singletonObjects.get(beanName);
if (null == singletonObject) {
singletonObject = earlySingletonObjects.get(beanName);
// 判断二级缓存中是否有对象
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 把三级缓存中的代理对象中的真实对象获取出来,放入二级缓存中
earlySingletonObjects.put(beanName, singletonObject);
singletonFactories.remove(beanName);
}
}
}
return singletonObject;
}
@Override
public void registerSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
singletonObjects.put(beanName, singletonObject);
earlySingletonObjects.remove(beanName);
singletonFactories.remove(beanName);
}
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory){
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
}
}
public void registerDisposableBean(String beanName, DisposableBean bean) {
disposableBeans.put(beanName, bean);
}
public void destroySingletons() {
Set<String> keySet = this.disposableBeans.keySet();
String[] disposableBeanNames = keySet.toArray(new String[0]);
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
String beanName = disposableBeanNames[i];
DisposableBean disposableBean = (DisposableBean) disposableBeans.remove(beanName);
try {
disposableBean.destroy();
} catch (Exception e) {
throw new BeansException("Destroy method on com.qingyun.springframework.aop.test.bean with name '" + beanName + "' threw an exception", e);
}
}
}
}
在用于提供单例对象注册的操作的 DefaultSingletonBeanRegistry 类中,共有三个缓存对象的属性; singletonObjects 、 earlySingletonObjects 、 singletonFactories如它们的名字一样,用于存放不同类型的对象(单例对象、早期的半成品单例对象、单例工厂对象)。
紧接着在这三个缓存对象下提供了获取、添加和注册不同对象的方法,包括:getSingleton 、 registerSingleton、addSingletonFactory,其实后面这两个方法都比较简单,主要是getSingleton 的操作,它是在一层层处理不同时期的单例对象,直至拿到有效的对象。
提前暴露对象
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
protected Object doCreateBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
Object bean = null;
try {
// 调用构造方法实例化Bean
bean = createBeanInstance(beanDefinition, beanName, args);
// 处理循环依赖,将实例化后的Bean对象提前放入缓存中暴露出来
if (beanDefinition.isSingleton()) {
Object finalBean = bean;
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));
}
// 实例化后判断
boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);
if (!continueWithPropertyPopulation) {
return bean;
}
// 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值
applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);
// 给 Bean 填充属性
if (beanDefinition.getPropertyValues() != null &&
beanDefinition.getPropertyValues().getPropertyValues().length != 0) {
applyPropertyValues(beanName, bean, beanDefinition);
}
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of com.qingyun.springframework.aop.test.bean failed", e);
}
// 注册实现了 DisposableBean 接口的 Bean 对象
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
// 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPE
Object exposedObject = bean;
if (beanDefinition.isSingleton()) {
// 获取代理对象
exposedObject = getSingleton(beanName, false);
if (exposedObject == null) {
exposedObject = bean;
}
registerSingleton(beanName, exposedObject);
}
return exposedObject;
}
// 此处有可能会提前进行动态代理完成AOP增强,但是也有可能返回原始对象
protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
Object exposedObject = bean;
for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
exposedObject = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).getEarlyBeanReference(exposedObject, beanName);
if (null == exposedObject) {
return exposedObject;
}
}
}
return exposedObject;
}
}
在 AbstractAutowireCapableBeanFactory#doCreateBean 的方法中主要是扩展了对象的提前暴露 addSingletonFactory 了,和单例对象的获取getSingleton 以及注册操作 registerSingleton 。
这里提到一点 getEarlyBeanReference 就是定义在如 AOP 切面中这样的代理对象,可以参考源码中接口InstantiationAwareBeanPostProcessor#getEarlyBeanReference 方法的实现。
项目代码Github地址:https://github.com/Zhang-Qing-Yun/mini-spring,本节代码对应的commit标识为7f8b6eb
欢迎标星