目录
二级缓存是否可以解决循环依赖,是否可以解决AOP这种特殊情况?
什么是循环依赖
循环依赖是指两个或多个Bean之间相互依赖形成的循环关系,举个a依赖b,b又依赖a的例子代码如下:
// BeanA.java
@Service
public class BeanA {
@Autowired
private BeanB beanB;
}
// BeanB.java
@Service
public class BeanB {
@Autowired
private BeanA beanA;
}
Spring是如何解决循环依赖的
Spring 获取bean的关键流程
从上图可以看出获取bean流程先从三级缓存中获取bean,如果存在返回,不存在走创建流程,具体的创建流程分为4步
- 实例化bean
- 将实例化bean添加到三级缓存中
- 依赖注入
- bean初始化
我们拿a->b,b->a例子走一遍流程
假设先获取beanA,发现缓存中不存在去创建beanA,首先实例化beanA,然后将beanA添加到三级缓存中,其次beanA依赖注入beanB,这个时候走获取beanB的流程,发现在缓存中不存在去创建beanB,首先实例化beanB,将beanB添加到三级缓存中,其次beanB依赖注入beanA,重新走获取beanA的流程,发现缓存中存在实例化的beanA,此时return,beanB依赖注入benaA成功,beanB初始化,获取beanB的流程走完,返回到上一级说明beanA依赖注入beanB成功,最后beanA初始化。这样就解决了循环依赖。流程图如下:
三级缓存是如何解决循环依赖的
我们看下从三级缓存中获取bean、将实例化bean添加到三级缓存以及bean初始化的代码
// 从三级缓存中获取bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1.一级缓存 缓存完整的单例bean(已经实例化且依赖注入且初始化好的bean)
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 2.二级缓存 缓存早期的单例bean(半成品bean,已经实例化,未依赖注入,未初始化)
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 3.三级缓存 缓存单例工厂(单利工厂内部是Bean的后置处理,主要考虑AOP这种情况)
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 执行单利工厂的逻辑
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
简单梳理下 先从一级缓存中获取bean,不存在从二级缓存中获取,不存在从三级缓存(单利工厂)中获取,同时删除单利工厂缓存
// 将半成品bean包装成单利工厂bean添加到第三级别缓存中
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);
}
}
}
// 将半成品bean(刚实例化)包装成单例工厂bean
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
// bean的后置处理器,AOP是基于Bean的后置处理器实现的
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
简单总结下,我所描述的将半成品bean添加到三级缓存中过程如下:1.将半成品bean包装成单利工厂 2.将单利工厂添加到第三级缓存中。
// bean的初始化过程 同时执行bean后置处理器,AOP代理机制是基于bean后置处理器实现的
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//在初始化之前执行后置处理器方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// bena的初始化
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 在初始化后执行bean的后置处理器
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
还是a->b, b->a的例子,假设a被aop代理了,那么b依赖的是a的代理对象,a依赖的是b,基于我上面分析的流程思考下该如何实现? 如何保证b依赖的是a的代理对象? 只需要将提前暴露的bean提前转化成代理对象。也就是获取a流程中的将半成品a包装成单利工厂并添加到第三级缓存这个步骤。
二级缓存是否可以解决循环依赖,是否可以解决AOP这种特殊情况?
其实循环依赖问题二级缓存是可以解决的,成品放在第一级缓存,半成品放在第二级缓存。 如果我们把三级缓存的第三级去掉就相当于把单利工厂的执行结果放到第二级缓存了,从代理的角度来看就相当于提前AOP了,从实际情况来讲,循环依赖很少出现,只有极个别的情况,如果只是为了极个别的情况就把所有bean的AOP提前,破坏了bean的整体创建流程。
如果把第二级缓存去掉,可能会出现代理对象不唯一,假设a->b, a->c , b->a, c-> a,a被aop代理了,此时会出现创建b对象时,会去寻找a,然后单利工厂执行一次,创建c时,回去寻找a,单利单利工厂又执行了一次,a产生了两个代理对象。