一、首先,看一下几种循环依赖
1、 构造器注入循环依赖
@Service
public class A {
public A(B b) {
}
}
@Service
public class B {
public B(A a) {
}
}
2 、singleton模式field属性 or setter注入循环依赖
@Service
public class A {
@Autowired
private B b;
}
@Service
public class B {
@Autowired
private A a;
}
3、 prototype模式field属性注入循环依赖
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class A {
@Autowired
private B b;
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class B {
@Autowired
private A a;
}
不能解决的情况:
- 构造器注入循环依赖
- prototype模式field属性注入循环依赖
能解决的情况:
- singleton模式field属性注入(setter方法注入)循环依赖
二、SpringBoot如何循环解决的呢?
就是通过三级缓存
//一级缓存,完整的单例对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//三级缓存,bean的ObjectFactory
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
//二级缓存,不完整的对象
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
首先说说Bean的生命周期
- 1、当我们Spring容器初始化完成后,就会通过ResourceLoader,解析注解等把Bean加载到容器中,生成BeanDifinition对象,然后通过BeanWrapper进行实例化。
- 2、populateBean()设置对象属性(执行aware接口啦,BeanBeforPostProcessor)
- 3、最后初始化IntializingBean,innit-method(BeanAfterPostProcessor),完成AOP代理
发生循环依赖的的地方就是在设置属性的地方populateBean(),详细过程如下
- 1、A在实例化之后,三级缓存addSingletonFactoris(beanName,lamda)这一步是放入的bean的ObejctFactory,然后去populate属性设置B。
- 2、B进行实例化,之后B的ObjectFactory也是放入三级缓存,然后去populate属性,发现B依赖A的,于是去一二三级缓存中找A,在三级缓存中找到了A的ObejctFactory,如果此时的A是aop那么返回的是一个proxybean,也就是提前让A的aop暴露,否则就返回自身,所以B中的属性A有两种情况A有AOP A实际上是一个代理类,否则就是A的实例,然后删除A三级缓存,放入二级缓存,然后B就初始化完成,放入一级缓存。
- 3、在返回A的生命周期来,从一级缓存获取B,属性设置完成,完成初始化。
三、关于为什么三级缓存?
添加三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));具体方法如下。它实际上就是调用了后置处理器的getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy注解导入的AnnotationAwareAspectJAutoProxyCreator。如果没有AOP那么直接返回原对象了,所以三级缓存肯定是用于AOP增强的。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
Iterator var5 = this.getBeanPostProcessors().iterator();
while(var5.hasNext()) {
BeanPostProcessor bp = (BeanPostProcessor)var5.next();
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
四、在给B注入的时候为什么要注入一个代理对象?
如果我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。这样的话,也不违背Spring bean生成流程,实例化-赋值-初始化-增强(AOP)。
五、明明初始化的时候是A对象,那么Spring是在哪里将代理对象放入到容器中的呢?
Object earlySingletonReference = this.getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
在完成初始化后,Spring又调用了一次getSingleton方法,这一次传入的参数又不一样了,false可以理解为禁用三级缓存,前面图中已经提到过了,在为B中注入A时已经将三级缓存中的工厂取出,并从工厂中获取到了一个对象放入到了二级缓存中,所以这里的这个getSingleton方法做的时间就是从二级缓存中获取到这个代理后的A对象。exposedObject == bean可以认为是必定成立的,除非你非要在初始化阶段的后置处理器中替换掉正常流程中的Bean,例如增加一个后置处理器。
六、一些思考
之前看了很多文章都没有把为啥一定要使用三级缓存的说得很清楚,我说下我自己的思考假设我们在这里直接使用二级缓存的话,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))那么意味着所有的Bean在这一步都要完成AOP代理,违背了Spring在结合AOP跟Bean的生命周期的设计,Spring结合AOP跟Bean的生命周期本身就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,先给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。
所以二级缓存模式是可以的,但是我觉得Spring没那么做还是考虑,规范性的问题。