解鞍卸甲——手写简易版Spring框架(终):使用三级缓存解决循环依赖问题

什么是三级缓存

按照目前我们实现的 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

欢迎标星

猜你喜欢

转载自blog.csdn.net/zhang_qing_yun/article/details/119983947