【十七】Spring IOC 三级缓存解决循环依赖

一、spring ioc的三级缓存

1.哪三级

singletonObjects:第一级缓存,里面放置的是已经实例化好的单例对象,是单例缓存池(singletonObjects)

earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象,早期对象(earlySingletonObjects)。简单粗暴的说就是new了对象了,但是这个对象还没填充属性;

singletonFactories:第三级缓存,里面存放的是将要被实例化的对象的对象工厂(存放 bean 工厂对象),是一个包裹对象ObjectFactory(registeredSingletons),通过getObject获取到早期对象。

2.作用:

spring ioc的三级缓存解决引用循环依赖,不解决构造方法注入的循环依赖,不解决非单例的循环引用

3.到底怎么解决的循环依赖

最简单的例子是,A引用了B,B引用了A。

直接上一个我觉得画得很好的创建bean的图,图来自:https://www.jianshu.com/p/f0c005c7354b

图片解释:

先看创建beanA,

第四步getSingleton从缓存中获取beanA,没有嘛,这才开始实例化嘞,缓存中没有咱就创建。

第八步就是创建了一个早期得beanA的对象。

第九步就是把这个早期对象放入缓存中,这里我理解的是放在二级缓存earlySingletonObjects中的。

第十步填充对象,这个时候就解析出来beanA依赖了beanB,那就要去创建beanB的对象才能给beanA填充上

再看创建beanB

第四步getSingleton从缓存中获取beanB,没有嘛,这才开始实例化嘞,缓存中没有咱就创建。

第八步就是创建了一个早期得beanB的对象。

第九步就是把这个早期对象放入缓存中,这里我理解的是放在二级缓存earlySingletonObjects中的。

第十步填充对象,这个时候就解析出来beanB依赖了beanA,这个时候getSingleton下能拿到二级缓存earlySingletonObjects早期暴露的beanA。

衍生问题:

1.为什么不能解决决构造方法注入的循环依赖?

从上面的流程可以看出,调用构造器创建实例是在第八步createBeanInstance方法,而解决循环依赖是在第十步populateBean这个方法中,执行顺序也决定了无法解决该种循环依赖

对于这种,如果喜欢使用lombok的@RequiredArgsConstructor注解的小伙伴就需要注意了,这个注解会生成一个带所有属性的构造方法。通过idea直接点开对应的class文件就可以看不看这个构造方法了。

2.为什么不能解决非单例对象的循环依赖?

非单例对象都没有放到这三级缓存中.

3.为什么一定要三级缓存,二级行不行?
不行,二级缓存不能解决代理对象之间的循环依赖。

详细解释:

首先我最前面写了:三级缓存是一个包裹对象ObjectFactory(registeredSingletons),通过调用它的getObject方法获取到早期对象,也就是得到那个所谓的二级缓存,没有填充的早期对象。

贴上图中第四步的DefaultSingletonBeanRegistry#getSingleton方法源码

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}

获取bean的时候,先从一级缓存(singletonObjects)中获取,也就是先看它是否已经实例化完了已经被spring管理起了,如果有就直接返回。

如果一级缓存没有,但是这个bean处于正在创建中,就从二级缓存(earlySingletonObjects)中拿,二级缓存有就返回二级缓存的。

如果二级缓存也没有,但是这个bean是可以允许早期引用的,那就从三级缓存(singletonFactories)中拿,这里三级缓存中拿出来的是个ObjectFactory,需要调用singletonFactory.getObject方法得到对象,这里调用singletonFactory.getObject方法得到得就直接放入二级缓存中了。

那么重点就是这个singletonFactory.getObject方法,我们可以看到它主要就是调用了

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference这个方法。

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
					if (exposedObject == null) {
						return null;
					}
				}
			}
		}
		return exposedObject;
	}

源码中可以看到实际上是调用的SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference方法

好,重点来了。

SmartInstantiationAwareBeanPostProcessor接口有两个主要的实现类,这次调用getEarlyBeanReference方法就根据不同的场景调用的这两个不同的实现类

如果是调到了InstantiationAwareBeanPostProcessorAdapter这个实现类,那就直接返回bean,这里看起来第三级缓存没必要存在,然而并不是

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		return bean;
	}

如果调用到了AbstractAutoProxyCreator这个实现类,那就体现了第三级缓存存在的意义了,第三级缓存的存在就是解决代理对象之间的循环依赖

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.contains(cacheKey)) {
			this.earlyProxyReferences.add(cacheKey);
		}
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

这里可以看到,一进来就先加入到了earlyProxyReferences缓存中,然后返回代理对象。

而在AbstractAutoProxyCreator类的postProcessAfterInitialization方法做代理时

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

而在AbstractAutoProxyCreator类的postProcessAfterInitialization方法做代理时,通过从earlyProxyReferences中看改对象是否早期就返回了代理,如果是那边表示此时的bean就是代理了的,直接返回bean。如果不是,才对这个对象做代理然后返回代理。

这里又避免了代理对象循环引用的时候出现同一个对象被代理多次的情况。

你可能感兴趣的:(Spring源码分析,三级缓存,循环依赖)