8.5 Spring解决循环依赖的机理(AOP)
MyAspect
@Aspect
public class MyAspect {
@After(value = "execution(* com.cjf.bean.B.*(..))")
public void myAfter(){
System.out.println("最终通知的功能.........");
}
}
SpringBean.xml
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="myAspect" class="com.cjf.bean.MyAspect"/>
<bean id="a" class="com.cjf.bean.A">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="com.cjf.bean.B">
<property name="a" ref="a"/>
</bean>
8.5.1、大致流程
-
先去一级缓存寻找
A
,没有去创建A
, 然后A
将自己的ObjectFactory
(lambda
)放入到三级缓存中,初始化的时候需要B
,去创建B
-
B
实例化同理A
(B
将自己的工厂对象(lambda
)放入到了 三级缓存),B
初始化的时候发现需要A
,于是B
先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A
的ObjectFactory
, -
然后调用
ObjectFactory.getObject()
方法最终会调用getEarlyBeanReference()
方法,返回一个A
的代理对象,然后把proxyA
放入二级缓存里面,并删除三级缓存中的ObjectFactoryA
-
B
顺利赋值完毕,通过populateBean
返回 ,然后调用initializeBean
方法进行初始化操作initializeBean
,bean
的声明周期操作,以及上述说明的第二种AOP
代理情况(代理B
对象)
-
然后回来接着创建
A
,此时B
已经创建结束,直接从一级缓存里面拿到proxyB
,完成注入操作及初始化。- 由于完成注入操作及初始化返回的是
A
对象,不是proxyA
。再从二级缓存中获取到proxyA
然后返回proxyA
- 由于完成注入操作及初始化返回的是
-
最后将
proxyA
放入到 一级缓存中,再从 二级缓存中删除proxyA
。
可以看出,更非 AOP
的循环依赖类似,只不过多了进行代理操作。
8.5.2、Spring AOP 代理时机
参考来自 Spring源码最难问题《当Spring AOP遇上循环依赖》_bugpool的博客-CSDN博客
参考来自 Spring 为何需要三级缓存解决循环依赖,而不是二级缓存 - 半分、 - 博客园 (cnblogs.com)
对于 Spring AOP
代理,Spring AOP
代理时机有2个:
-
当自定义了
TargetSource
,则在bean
实例化前完成Spring AOP
代理并且直接发生短路操作,返回bean
-
正常情况下,都是在
bean
初始化后进行Spring AOP
代理 ② -
如果要加上提前曝光代理,
getEarlyBeanReference
可以说3
种 ③
第二种,initializeBean
(初始化 bean
)
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 调用bean初始化后置处理器处理
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
调用 postProcessAfterInitialization
(后置处理器的)方法进行代理
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 获取缓存key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 查看该bean是否被Spring AOP提前代理!而缓存的是原始的bean,因此如果bean被提前代理过,这此处会跳过
// 如果bean没有被提前代理过,则进入AOP代理
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//返回一个新的代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
第三种,提前曝光代理
getEarlyBeanReference
方法
//AbstractAutowireCapableBeanFactory.java
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
//返回一个生成的代理对象
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
getEarlyBeanReference
方法(不同的类),在这里 A
提前代理了。
// AbstractAutoProxyCreator.java
public Object getEarlyBeanReference(@Nullable Object bean, String beanName) {
// 获取缓存key
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
//将当前对象放入 earlyProxyReferences 中
this.earlyProxyReferences.put(cacheKey, bean);
return this.wrapIfNecessary(bean, beanName, cacheKey);
return bean;
}
返回一个新的代理对象。
8.5.3、提前曝光 A,创建 proxyA
情节说明(我们代理 A
和 B
):
- 先去一级缓存寻找
A
,没有去创建A
, 然后A
将自己的ObjectFactory
(lambda
)放入到三级缓存中,初始化的时候需要B
,去创建B
B
实例化同理A
(B
将自己的工厂对象(lambda
)放入到了 三级缓存),B
初始化的时候发现需要A
,于是B
先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A
的ObjectFactory
,- 然后调用
ObjectFactory.getObject()
方法最终会调用getEarlyBeanReference()
方法,返回一个A
的代理对象,然后把proxyA
放入二级缓存里面,并删除三级缓存中的ObjectFactoryA
这里。通过上面第 3 3 3 种 提前曝光处理,创建 A 的代理对象
一层一层的返回 ,sharedInstance
对象为 A
的代理对象( proxyA
),然后对获取到的 proxyA
赋值处理,
- 即:
B
(
a
=
A
(
b
=
n
u
l
l
)
)
B(a=A(b=null))
B(a=A(b=null)),但是这里的
A
为proxyA
8.5.4、后置处理器创建 proxyB
此时:
B
顺利赋值完毕,通过populateBean
返回 ,然后调用initializeBean
方法进行初始化操作
initializeBean
,bean
的生命周期操作,以及上述说明的第二种AOP
代理情况
对 B 对象进行后置处理器创建代理,返回 proxyB
B
对象已经初始化完成, 此时exposedObject
为proxyB
,
将 proxyB
对象加入到一级缓存中( addSingleton(beanName, proxyB);
)
- 然后回来接着创建
A
,此时B
已经创建结束,直接从一级缓存里面拿到proxyB
,完成对A
对象的注入,以及对 原始A
对象初始化操作(initializeBean
)
// 6. 存在提前曝光情况下
if (earlySingletonExposure) {
// earlySingletonReference:二级缓存,缓存的是经过提前曝光提前Spring AOP代理的bean
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// exposedObject跟bean一样,
// 说明初始化操作没用应用 getEarlyBeanReference (指AOP操作) 改变 exposedObject
// 主要是因为exposedObject如果提前代理过,就会跳过Spring AOP代理,
// 所以exposedObject没被改变,也就等于bean了
if (exposedObject == bean) {
// 将二级缓存中的提前AOP代理的bean赋值给exposedObject,并返回
exposedObject = earlySingletonReference;
}
// 引用都不相等了,也就是现在的bean已经不是当时提前曝光的bean了
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// dependentBeans也就是B, C, D
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// 被依赖检测异常
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(...);
}
}
}
}
对于我们这里:
此时 exposedObject
为 proxyA
,
最后将 proxyA
对象加入到一级缓存中( addSingleton(beanName, proxyB);
)删除二级缓存中的 proxyA
完成 proxyA
对象的创建
8.5.5、注意事项
通过三级缓存拿到 ObjectFactory
对象后,调用 ObjectFactory.getObject()
方法最终会调用getEarlyBeanReference()
方法,我们会发现再执行一遍 singletonFactory.getObject()
方法又是一个新的代理对象,这就会有问题了,
- 因为
A
是单例的,每次执行singletonFactory.getObject()
方法又会产生新的代理对象, - 假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到
singleFactory
对象,执行getObject()
方法又会产生新的代理对象,这是不行的,
因为 A
是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行 singletonFactory.getObject()
产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍 singletonFactory.getObject()
方法再产生一个新的代理对象,保证始终只有一个代理对象。
还有一个注意的点
- 既然
singletonFactory.getObject()
返回的是代理对象,那么注入的也应该是代理对象,我们可以看到注入的确实是经过CGLIB
代理的A
对象。 - 所以如果没有
AOP
的话确实可以两级缓存就可以解决循环依赖的问题, - 如果加上
AOP
,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()
方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。
绝大部分情况下,都是在 initializeBean
(初始化 bean
)进行创建代理对象的。只有循环依赖的时候,才会用到三级缓存,进行提前代理对象,也就是 getEarlyBeanReference