更多文章请观看:[www.shangsw.com]
一 Spring三级缓存解决循环依赖原理
1.1 什么是三级缓存
什么是循环依赖我们在前面Spring源码部分已经解读过了,具体可以看:
3、bean的加载 中的1.6部分
我们之前在阅读Spring源码的文章当中,在getBean的过程中,调用的doGetBean处有这样一段代码:
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = transformedBeanName(name);//提取对应的beanName
Object bean;
Object sharedInstance = getSingleton(beanName);
//省略
return (T) bean;
}
而这个getSingleton(beanName) 就是三级缓存的体现,追踪过去的代码是这样的:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//1
Object singletonObject = this.singletonObjects.get(beanName);//获取完整的单例对象(一级缓存)
//一级缓存没有获取到单例对象并且这个对象正在创建中情况(singletonsCurrentlyInCreation缓存beanName) Start
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);//获取早期的单例对象(没有属性注入, 即二级缓存)
//没有获取到早期的单例对象(二级缓存)并且允许创建早期的引用 Start
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {//全局变量Map, 存在并发访问问题, 写入需要加锁
singletonObject = this.singletonObjects.get(beanName);//双重锁检查保证单例的唯一性
if (singletonObject == null) {//不存在
//2
singletonObject = this.earlySingletonObjects.get(beanName);//获取早期的单例对象
if (singletonObject == null) {//早期的单例对象也不存在
//3
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);//获取单例的对象工厂
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();//单例对象
this.earlySingletonObjects.put(beanName, singletonObject);//存放早期的单例对象(没有属性注入)
this.singletonFactories.remove(beanName);//从对象工厂的缓存中删除
}
}
}
}
}
//没有获取到早期的单例对象(二级缓存)并且允许创建早期的引用 End
}
//一级缓存没有获取到单例对象并且这个对象正在创建中情况(singletonsCurrentlyInCreation缓存beanName) End
return singletonObject;//存在直接返回
}
在这里的1、2、3处位置都使用了Map缓存,这三处就是对应了三级缓存,对应的三个field如下:
/** 一级缓存, 缓存的是beanName和完整的单例对象的缓存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 三级缓存, 缓存的是beanName和ObjectFactory的关系 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 二级缓存, 缓存beanName和单例对象的早期对象, 即没有属性注入的对象 */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
接着看上面的代码:
-
1处:在最上层的缓存singletonObjects中,缓存的是完整的单例bean,这里面拿到的bean是已经实例化好和初始化好的bean,可以直接使用的bean。如果没有取到,则进入2处
-
2处:在二级缓存earlySingletonObjects中,查找bean。二级缓存主要缓存的分成两种情况,只要看这个bean是否被AOP切面处理:
- 否:保存半成品的bean原始实例,属性未填充
- 是:保存的是代理的bean,即proxyBean,目标bean还是未填充属性的半成品
-
3处:如果在二级缓存中,还是没有找到,则在三级缓存中查找对应的工厂对象,利用拿到的工厂对象(工厂中有3个field,一个是beanName,一个是RootBeanDefinition,一个是已经创建好的,但是还没有注入属性的bean)去获取包装后的bean,或者说,代理后的bean
那么什么是创建好的,但是没有注入属性的bean?
比如一个bean,有10个字段,如果只是new之后,对象是有了,内存空间已经开辟,堆里已经分配好了该对象的空间,但是此时这10个field还是null。所以称为半成品的bean。
那么Spring是怎么利用这三级缓存解决循环依赖问题的呢?这要从Spring的bean的生命周期说起
1.2 Spring Bean的生命周期
我们先完成的看一遍Spring getBean的整体流程:
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = transformedBeanName(name);//提取对应的beanName
Object bean;
Object sharedInstance = getSingleton(beanName);//从缓存获取, 首次创建肯定获取不到
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
} else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {//不存在
//忽略的代码
try {
//忽略的代码
//实例化依赖的bean后便可以实例化mbd本身了
if (mbd.isSingleton()) {//singleton模式的创建
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//else if忽略的代码
} catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
//忽略的代码
return (T) bean;
}
其他的代码我这里都注释成忽略的代码了,我们主要关注createBean部分,这个是bean的生命周期的关键函数。
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {
//忽略的代码
//给BeanPostProcessors一个机会来返回代理来替代真正的实例 Start
try {
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
} catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
//给BeanPostProcessors一个机会来返回代理来替代真正的实例 End
//创建并返回bean实例 Start
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//忽略的代码
return beanInstance;
} catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
//创建并返回bean实例 End
}
这里主要有两个部分,第一部分是resolveBeforeInstantiation函数部分,主要是利用BeanPostProcessor改变当前的最原始配置的bean,当然,这里不是我们要看的内容。第二部分主要是doCreateBean函数的创建bean实例的部分。
- 进入doCreateBean函数:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {
//Bean的包装器获取 Start
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {//单例模式
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);//删除缓存
}
if (instanceWrapper == null) {//缓存中不存在, 则直接创建Bean包装器
//根据指定的bean使用对应的策略创建新的实例, 如: 工厂方法、构造函数自动注入、简单初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//Bean的包装器获取 End
//忽略代码
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//忽略代码
//为避免后期循环依赖, 可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//初始化bean实例
Object exposedObject = bean;
try {
//对bean进行填充, 将各个属性值注入, 其中, 可能存在依赖其他bean的属性, 则会递归初始依赖bean
populateBean(beanName, mbd, instanceWrapper);
//调用初始化方法: init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
//忽略代码
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {//earlySingletonReference只有在检测到有循环依赖的情况下才会不为空
if (exposedObject == bean) {//如果exposedObject没有在初始化方法中被改变, 也就是没有被增强
exposedObject = earlySingletonReference;
} //忽略else if代码
}
}
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);//根据scope注册bean
} catch (BeanDefinitionValidationException ex) {
//忽略代码
}
return exposedObject;
}
从这个函数开始时生命周期的主要内容:
- 首先createBeanInstance函数,即创建bean实例,bean开始。此时这个bean只是被创建了,类似new了一个空的构造器。属性等内容还没有注入
- 第二步是addSingletonFactory和getEarlyBeanReference部分,当然,这一部分跟生命周期无关,主要是解决循环依赖的,暂时略过
- 第三步是populateBean函数,主要是对各个属性值进行依赖注入,填充了上面的空bean的属性
- 第四步是调用其init(如果存在的话)方法初始化
- registerDisposableBeanIfNecessary调用销毁的方法(如果需要的话)
通过上面代码的查看,其实bean的生命周期可以大致分为以下几个过程:
- 创建bean实例
- 属性注入
- init初始化
- 销毁
经过上述的bean的生命周期介绍以及大致的代码描述(具体的代码我这里就不再一次阅读了,可以看上述的几篇文章),我们继续看Spring如何利用三级缓存解决循环依赖的。
1.3 三级缓存解决循环依赖
我们以这样一个示例:
- AService -> BService
- BService -> AService
AService依赖BService,BService也依赖AService,加入在getBean(aService)的时候,进入doCreateBean:
Spring首先是加个三级缓存,里面存的不是具体的bean,而是一个工厂对象,是可以拿到代理的bean的。
在上述的doCreateBean中有一段如下代码:
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//忽略代码
//为避免后期循环依赖, 可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
这里new了一个ObjectFactory,然后会存入到如下的第三级缓存中:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {//加锁
if (!this.singletonObjects.containsKey(beanName)) {//一级缓存不存在
this.singletonFactories.put(beanName, singletonFactory);//添加缓存(三级缓存)
this.earlySingletonObjects.remove(beanName);//删除早期的对象(二级缓存)
this.registeredSingletons.add(beanName);//添加注册的单例(表示已注册)
}
}
}
注意,这里是new一个匿名内部类的对象,注意这里的ObjectFactory是一个匿名内部类,具体是通过getEarlyBeanReference()获取的,进入这个方法:
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;
}
这个方法我们发现其实如果存在BeanPostProcessor的话,就会通过getEarlyBeanReference获取其对象。那么这个BeanPostProcessor在这一步中有什么作用呢?
其实我们之前已经介绍过,在 5、AOP 文章中,其实我们知道AOP的织入是通过BeanPostProcessor进行织入的,所以这里其实主要是BeanPostProcessor的AOP处理,主要获取的是代理对象proxyBean。
所以此时,我们知道三级缓存中存放的是AService的ObjectFactory的,而ObjectFactory中存储的bean是AService的代理对象。
代码继续往下走,走到属性填充,发现有依赖BService,那么创建BService(此时AService还在等着完成创建),BService以同样的逻辑走到属性填充这,发现依赖AService,那么走getBean的逻辑,走到doGetBean的代码,如下:
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = transformedBeanName(name);//提取对应的beanName
Object bean;
Object sharedInstance = getSingleton(beanName);
//省略
return (T) bean;
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);//获取完整的单例对象(一级缓存)
//一级缓存没有获取到单例对象并且这个对象正在创建中情况(singletonsCurrentlyInCreation缓存beanName) Start
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);//获取早期的单例对象(没有属性注入, 即二级缓存)
//没有获取到早期的单例对象(二级缓存)并且允许创建早期的引用 Start
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {//全局变量Map, 存在并发访问问题, 写入需要加锁
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);//从对象工厂的缓存中删除
}
}
}
}
}
//没有获取到早期的单例对象(二级缓存)并且允许创建早期的引用 End
}
//一级缓存没有获取到单例对象并且这个对象正在创建中情况(singletonsCurrentlyInCreation缓存beanName) End
return singletonObject;//存在直接返回
}
走到这,其实我们已经知道BService属性填充AService时,通过三级缓存拿到其ObjectFactory,然后getObject()方法获取到其代理对象。这个getObject就是上文中提到的匿名内部类getEarlyBeanReference获取的对象,即代理对象AService。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {
//忽略代码
if (earlySingletonExposure) {
//忽略代码
//为避免后期循环依赖, 可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//忽略代码
return exposedObject;
}
此时BService完成AService的注入,BService变成一个完整的对象,且是经过BeanPostProcessor改变后的代理对象。并存放到了一级缓存中。
继续走AService的属性注入流程,此时BService的代理对象已经创建完成并返回,AService注入BService时BService其实是一个完整的代理对象。AService注入并放入一级缓存,这样就完美的解决了循环依赖的问题。
最后我们总结一下上面的创建流程:
- AService首先实例化,实例化通过ObjectFactory半成品暴露在三级缓存中
- 填充属性BService,发现BService还未进行加载,就会先去加载BService
- 在加载BService过程中,实例化,通过ObjectFactory半成品暴露在三级缓存中
- 填充属性AService的时候,这时候能够从三级缓存中拿到半成品的ObjectFactory
- 拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法,此方法最终会调用到getEarlyBeanReference()方法,这个方法主要逻辑是如果bean被AOP切面代理则返回的是proxyBean,如果未被代理则返回的是原始originBean。
- 此时拿到AService的半成品代理bean,然后从三级缓存中移除,放到二级缓存earlySingletonObjects中,此时BService注入的AService其实是一个半成品的AService代理对象(原始对象),最终BService完成的创建,
- 回来继续走AService的属性填充,最终完成的BService会被注入进去,AService完成创建
- 最终BService的属性AService也是一个完整的对象,因为他们是同一个对象
1.4 为什么用三级缓存而不是二级缓存
其实到这里,很多人想为什么不用二级缓存,而非得用三级缓存才能解决循环依赖呢。
二级缓存有两种组合方式:
- singletonObjects和earlySingletonObjects
- singletonObjects和singletonFactories
不管哪种组合方式,我们看上面的示例(AService依赖BService,BService同样依赖AService)能否走通就可以了。
1.4.1 singletonObjects和earlySingletonObjects组合
我们直接看流程:
- AService创建,放入早期的earlySingletonObjects缓存,缓存的是半成品AService对象。走到属性注入BService
- 发现BService没有创建,开始创建BService
- 创建BService,属性注入AService,首先从缓存中获取AService,能获取到半成品的AService,进行注入到BService的字段中,BService完成创建并返回。删除earlySingletonObjects并添加singletonObjects缓存
- 继续走AService注入属性的流程,拿到完整的BService对象,注入到对象中去。然后删除earlySingletonObjects缓存并添加singletonObjects缓存
- 此时BService拿到的AService对象是一个完整的对象,因为他们是同一个对象
1.4.2 singletonObjects和singletonFactories组合
同样,我们也直接看流程
- AService创建,拿到ObjectFactory放入singletonFactories缓存中,缓存的同样是半成品的AService的ObjectFactory,走到属性注入BService
- 发现BService没有创建,开始创建BService
- 创建BService,属性注入AService,首先从缓存中singletonFactories能获取到AService的ObjectFactory,然后调用其getObject函数拿到AService,此时AService同样是个半成品的,注入到BService的字段中,BService完成创建并返回。删除singletonFactories并添加singletonObjects缓存
- 继续走AService注入属性的流程,拿到完成的BService对象,注入到对象中去,然后删除singletonFactories缓存并添加singletonObjects缓存
- 此时BService拿到的AService对象是一个完整的对象,因为他们是同一个对象
不管是哪一种组合,我们看这个流程似乎都没有问题,也确实,这种流程走下来确实是没有问题的。但是我们忽略了一个重要的特性:AOP。
1.4.3 存在AOP的情况
存在AOP的情况,上述的组合的流程就不太一样了。我们先看singletonObjects和singletonFactories组合的情况。
我们都知道AOP其实是通过BeanPostProcessor的postProcessAfterInitialization织入的,并且我们在存在AOP代理的情况下,ObjectFactory.getObject()最终调用的是getEarlyBeanReference方法:
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;
}
而如果存在AOP代理的情况下,会调用SmartInstantiationAwareBeanPostProcess的getEarlyBeanReference方法,我们看其父类AbstractAutoProxyCreator的方法:
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
我们可以看到先生成缓存key,再存入缓存,最后调用wrapIfNecessary方法,wrapIfNecessary这个方法我们似乎很熟悉,在AOP的一章节中,发现AOP的postProcessAfterInitialization调用的就是这个方法,具体实现是在AbstractAutoProxyCreator中:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);//构建缓存的Key
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);//如果适合被代理, 则需要封装指定的bean
}
}
return bean;
}
是的,他们最终走到的都是wrapIfNecessary方法,但是整个加载流程只会走一次,因为这里有一个缓存key。现在理解这个缓存的作用了。
那么我们继续看,拿到的ObjectFactory其实是一个匿名内部类,内部类的bean是AOP代理的bean。每次getObject的时候其实是走AOP代理的逻辑,那么这里就有问题了,如果每次都ObjectFactory.getObject()都是一个新的对象,前后无法关联,最终导致BService注入的AService并不是一个完整的AService。所以,此时我们可以否定singletonObjects和singletonFactories的组合了。
1.4.3.1 存在AOP的singletonObjects和earlySingletonObjects
这种情况同上述的情况不同,上述的singletonFactories是因为创建出了不同的对象,但是earlySingletonObjects这是这两级缓存的话,貌似不管从设计上还是从代码上来看都没有任何问题。具体我们可以修改Spring的源码调试一下就知道了。
(1) 去掉三级缓存, 只使用一级和二级缓存
我们可以直接修改Spring的源码,将三级缓存singletonFactories去掉,具体修改的位置有:
- org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean),从缓存获取对象,修改成如下形式:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
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;//存在直接返回
}
其中注释掉的就是三级缓存, 也就是Spring源码。
(2) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton,删除三级缓存对象。这里可改可不改,不影响整体的测试流程:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
// this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
(3) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory,添加三级缓存的地方,修改成如下:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// this.singletonFactories.put(beanName, singletonFactory);
// this.earlySingletonObjects.remove(beanName);
this.earlySingletonObjects.put(beanName, singletonFactory.getObject());
this.registeredSingletons.add(beanName);
}
}
}
注意,这里是关键,我们直接将三级缓存去掉,而是直接调用singletonFactory.getObject()存放缓存。而这个ObjectFactory我们知道是从getEarlyBeanReference获取到的匿名内部类。所以存进去的二级缓存直接是早期的未填充属性的对象。Spring源码改完以后,我们可以测试一下。
首先,我们创建两个测试类,循环依赖了AService和BService,如下:
public class AService {
private BService bService;
private CService cService;
public AService() {
}
public BService getbService() {
return bService;
}
public void setbService(BService bService) {
this.bService = bService;
}
public void test() {
System.out.println("AService");
}
}
public class BService {
private AService aService;
public BService() {
}
public AService getaService() {
return aService;
}
public void setaService(AService aService) {
this.aService = aService;
}
public void test() {
System.out.println("BService");
}
}
此时,我们先不做AOP代理,看看测试效果。配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<aop:aspectj-autoproxy/>
<bean id="aService" class="org.springframework.aop.circle.bean.AService">
<property name="bService" ref="bService"/>
</bean>
<bean id="bService" class="org.springframework.aop.circle.bean.BService">
<property name="aService" ref="aService"/>
</bean>
</beans>
测试类如下:
public class CircleAopTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("org/springframework/circle/circleSpringAOPTest.xml");
AService aService = (AService) context.getBean("aService");
aService.test();
aService.getbService().test();
}
}
运行结果如下:
AService
BService
发现测试效果跟我们预期的一样。我们再加入AOP的情况测试,添加AOP:
@Aspect
public class ServiceAOP {
@Pointcut("execution(* org.springframework.aop.circle.bean.*.*(..))")
public void pointcut(){}
@Before("pointcut()")
public void beforeTest() {
System.out.println("beforeTest");
}
@After("pointcut()")
public void afterTest() {
System.out.println("afterTest");
}
}
上面的AOP对AService和BService都进行了代理横切,配置文件需要改成如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<aop:aspectj-autoproxy/>
<bean id="aService" class="org.springframework.aop.circle.bean.AService">
<property name="bService" ref="bService"/>
</bean>
<bean id="bService" class="org.springframework.aop.circle.bean.BService">
<property name="aService" ref="aService"/>
</bean>
<bean class="org.springframework.aop.circle.aops.ServiceAOP"/>
</beans>
测试再运行测试类,效果如下:
beforeTest
AService
afterTest
beforeTest
afterTest
beforeTest
BService
afterTest
效果也确实是我们需要的效果。所以最终我们通过getEarlyReference方法获取对象的代理对象,如果去掉三级缓存,那么如果手动调用一下这个方法不也照样没有问题吗?为什么Spring添加一个三级缓存呢?其实我们可以自行猜测一下:
- 问题就在于调用getEarlyReference,这一步在很多时候其实是没有必要的,在没有循环依赖的时候,这个方法是不会被调用。也就是说,我们为了解决系统中那百分之一的可能,而让99%的bean创建的时候都走上一圈。效率上说不过去
- Spring官方建议我们使用构造器方式注入,而使用构造器注入我们知道是无法解决循环依赖问题的。这么看的话其实不是Spring的自相矛盾吗?所以猜测Spring是不希望我们使用Spring的循环依赖的,因为能在项目启动中就爆出问题远远比在项目运行后不知道什么时候爆出问题的严重程度要小。
- 方便后续的扩展
所以说,从功能上来说,完全可以使用二级缓存(singletonObjects和earlySingletonObjects)解决循环依赖的问题,但是出于效率性和扩展性来说,Spring采用了三级缓存解决这个问题。