问题描述
循环依赖其实就是两个类相互依赖,它们各自都需要去获取对方的实例,而导致的问题。
public class A {
public A() {
}
private B b;
public void setB(B b) {
this.b = b;
}
}
B对象
public class B {
private A a;
public B() {
}
public void setA(A a) {
this.a = a;
}
}
xml配置文件
<bean id="a" class="com.pri.spring5.demo.A">
<property name="b" ref="b"></property>
</bean>
<bean id="b" class="com.pri.spring5.demo.B">
<property name="a" ref="a"></property>
</bean>
如下图所示:
Spring是如何解决的
三级缓存
- 一级缓存: 存放已经经过了完整的生命周期的bean;
- 二级缓存: 存放需要早期曝光的bean,该bean是一个尚未初始化完成的半成品对象(一般只有处于循环引用状态的Bean才会被保存在该缓存中);
- 三级缓存: 从源码中可以看到,三级缓存并不像一、二级缓存直接存放bean对象,而是存放一个ObjectFactory函数式接口对象,该接口只有一个方法,
getObject
,我们可以通过这个方法获取对应的bean。spring默认实例化bean之后,在下一步就会将该bean存入三级缓存中。
内部实现过程
流程图
基于上述的A、B案例,我这里画了一个详细的流程图。
刚开始看的话,图比源码更直观。
内部重点实现方法
先从doGetBean方法开始,在里面分别有两个getSingleton
方法,注意,这两个方法不是同一个方法!!而且很重要!!
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
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 {
if (mbd.isSingleton()) {
// 重点方法
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
}
return (T) bean;
}
来看看第一个getSingleton
方法的内部实现
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存中获取该bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从二级缓存中获取该bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 从三级缓存中获取该bean对应的singletonFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 返回该bean对象(三级缓存中)
singletonObject = singletonFactory.getObject();
// 将该bean放到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存中删除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
从而可知,每次调用
doGetBean
方法的时候,都需要先从缓存中获取对象,如果缓存中没有的话,再去创建。
再看看第二个getSingleton
方法,先来看看它是如何调用的?
getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// ...
}
});
如果你不懂lambda表达式,其实上述的代码就等同于—>
getSingleton(beanName, new ObjectFactory() {
@Override
public T getObject() {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// ...
}
}
});
再来看看这个getSingleton
方法的具体实现
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 将当前bean添加到singletonsCurrentlyInCreation中,表示该bean正在经历创建过程中,后面会用到判断(比如上述的getSingleton中if的判断)
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
try {
// 其实就是调用createBean方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (BeanCreationException ex) {
// ...
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 将创建完的bean添加到一级缓存,并且从二、三级缓存中删除(执行到这里说明该bean已经执行完其生命周期了)
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
其中的addSingleton
,将创建完的bean添加到一级缓存,并且从二、三级缓存中删除(执行到这里说明该bean已经执行完其生命周期了)
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 将该bean添加到一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 从三级缓存中删除
this.singletonFactories.remove(beanName);
// 从二级缓存中删除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
doCreateBean
:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 实例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// ...
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 注意这里,在实例化bean之后,会将该bean存入三级缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// ...
return exposedObject;
}
注意,在上面执行完createBeanInstance
方法后(通过反射去创建该bean的实例),接着就去执行了addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))
方法,也就是将存入三级缓存的操作。
如果你不懂lambda,这段代码等同于:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// ==================》等同于
addSingletonFactory(beanName, new ObjectFactroy() {
@Override
public T getObject() {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
开头介绍就提过,三级缓存不直接存放对象,而是存放一个lambda表达式,但是通过这个getObject方法,可以获取到对象。具体为什么这么实现,这关系到循环依赖是否涉及到动态代理,下面会解释。
下面具体的来看一下doCreateBean
中的populateBean
方法,也就是给对象注入属性的方法
populateBean ---> applyPropertyValues ---> resolveValueIfNecessary --> resolveReference
:
注意这个方法,在给当前对象注入对象属性的时候,我们发现,在经过了一列的方法调用后,最后会去执行getBean方法,尝试着从容器中获取这个对象。
- 比如说,在上述的例子中,先去实例化A并且给A注入属性,那么就会执行到这里,尝试着通过getBean方法去容器中获取一个B对象;
- 这个时候,再回到doGetBean方法,上述提到了doGetBean方法在开始就会先去缓存中获取对象,而这个时候缓存中是不存在对象B的,所以下一步就是实例化B对象,然后给B对象注入属性(这里就和A的过程一样了);
- 给B注入属性的过程中,必然也会执行到getBean里,尝试去容器中获取A对象,那么又执行到doGetBean,开头先去缓存中查找该对象,因为前面实例化A对象之后,就会存入三级缓存(上面说了),所以这里从三级缓存中就可以拿到一个A对象,这样就完了对象B的注入。
大概的执行过程就是这样,具体的请看流程图。
为什么要有第三级缓存?
在上述的例子中,我们并没有提到使用AOP下循环依赖的情况。
那么,现在考虑一下,假设A对象有一个对应的代理对象,也就是实现了AOP。我们在创建B对象时,给其注入A属性,这个注入的属性对象必须是A的代理对象吧!
首先,你需要知道,一般情况下,某个类的代理对象是在什么时候生成的?
在其进行初始化操作,执行后置处理器的时候,如果该类实现了AOP,则在这一步才会生成代理对象。
那么,基于这种情况,看看下面的例子。
假设没有第三级缓存
执行步骤:
- 首先,实例化A对象,将其直接放入二级缓存中(原本这里是放入三级缓存的);
- 注入A对象的属性,发现需要一个B对象;
- 实例化B对象,将其直接放入二级缓存中(原本这里是放入三级缓存的);
- 注入B对象中的A属性;
这个时候,发现问题了。二级缓存中保存的是一个A的半成品对象,但是A的代理对象还没有生成(A对象才执行到半路就去创建B对象了),我们在对B对象中的A属性进行注入的时候,只能从二级缓存中获取到这个普通的A对象,而并非A的代理对象。
这个时候三级缓存诞生了。
我们都有注意到,三级缓存中存放的是一个ObjectFactory对象,其实就是一个lambda表达式。
getEarlyBeanReference
重点来说一下这个方法。
前面说到,一般情况下,bean的代理对象都是在执行后置处理器的时候才会生成。那么如果遇到上面这种情况,那么B对象在注入A属性的时候根本无法获取到A的代理类(A本身就没有执行到后置处理器这一步,还没生成代理对象呢)。
因此,Spring提供了一个getEarlyBeanReference
方法,如果对象实现了AOP,则调用该方法直接就能返回其代理对象。
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;
// 创建并返回该bean的代理对象
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
并且这个方法作为lambda表达式保存到了三级缓存中。
这个时候,步骤就变成了这样:
-
首先,实例化A对象,将其直接放入三级缓存中(value: lambda表达式);
-
注入A对象的属性,发现需要一个B对象;
-
实例化B对象,将其直接放入三级缓存中;
-
注入B对象中的A属性,直接从三级缓存中获取到这个lambda表达式,然后调用
getEarlyBeanReference
,提前就把A的代理对象拿出来了,并且将这个代理对象放入二级缓存中,从三级缓存中删除; -
进行注入操作,将获取到的A的代理对象注入到B对象的属性中;
-
此时B对象已经是一个完整的对象了;
-
这个时候再回来,继续初始化A对象;
-
A对象拿到刚刚返回的B对象(完整的B对象),对其属性B进行注入
注意,这里的A对象还是之前那个普通的A对象,并不是其代理对象。
-
A对象完成属性的注入,再去执行后置处理器生成代理对象时,就不需要带去做别的操作了,直接从二级缓存中拿刚刚的那个代理对象即可;
-
初始化完毕,对象保存到一级缓存,情况二三级缓存。
流程图:
仅仅使用二级缓存行不行?
如果循环依赖的对象不存在生成AOP,也就是不去调用getEarlyBeanReference方法时,完全可以。
这个时候,又听到了一个问题,直接将ObjectFactory保存到二级缓存中不就行了吗?
答案:也是可以的。
但是
如果是这样,那么在进行创建A对象的操作过程中,就需要提前执行它的后置处理器获取其代理对象(也就是在创建完实之后,就去获取它的代理对象),然后再去注入其属性,创建B对象的流程。
我的理解是,这样做完全打乱了Bean的生命周期了。