spring中对于循环依赖的解决采用了三级缓存机制,即:
Map<String, Object> singletonObjects //第一级缓存,存放初始化完成的bean
Map<String, Object> earlySingletonObjects //第二级缓存,存放半成品bean
Map<String, ObjectFactory<?>> singletonFactories //第三级缓存,存放bean的工厂类
下面通过源码分析创建循环依赖bean的流程,假设存在循环依赖的bean如下:
@Service
class BeanA {
@Autowired
BeanB beanB;
public void test() {
System.out.println("test");
}
}
@Service
class BeanB {
@Autowired
BeanA beanA;
}
@Aspect
@Component
public class UserAspect {
@Pointcut("execution(* com.tao.spring.*.*(..))")
public void addLog() {}
@Before("addLog()")
public void before() {
System.out.println("before");
}
}
首先从获取BeanA开始,创建bean的入口处在AbstractBeanFactory.doGetBean:
该方法中试图通过getSingleton获取bean实例,看下该方法内容:
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
首先从一级缓存中获取,获取不到再从二级缓存中获取,二级缓存中没有再从三级缓存中获取。开始时缓存中都没有BeanA、BeanB的信息,故获取不到bean实例,准备创建bean,入口在AbstractAutowireCapableBeanFactory.doCreateBean,下面来分析下该流程:
首先会创建raw bean,此时创建的beanA只是一个空壳,其中的属性为空
接下来构造一个bean的工厂并缓存到第三级缓存中,执行到这一步时,第一、第二级缓存仍为空,第三级缓存中存储了BeanA的工厂对象。
接着走到populateBean方法,该方法需要初始化beanA的成员,由于beanA中包含beanB,故需要创建beanB,继续走到doCreateBean方法,此时传入的beanName参数为beanB,仍然会创建一个raw bean,此时创建的beanB只是一个空壳,其中的属性为空
同样会将beanB的工厂对象缓存到第三级缓存中
接下来继续走到populateBean方法中,即beanB需要获取注入的beanA对象,最终走到getSingleton方法中:
beanB试图通过该方法获取beanA的实例,由于第三级缓存中有beanA的工厂对象,故通过工厂对象创建beanA的实例并加入到第二级缓存中,注意此时创建的beanA实例对象是经过spring代理的对象,beanA的中beanB此时仍然为null,执行完方法populateBean之后,beanB已经包含了beanA的实例对象(虽然此时beanA尚未初始化完成,但这并不妨碍beanB的初始):
beanB初始化完成之后,加入到一级缓存中
beanA执行完populateBean方法之后,beanB已经初始化完成,并且注入到beanA中
但注意到此时exposedObject仍然是beanA的raw实例,并非前面创建的代理对象实例,而最终需要注入一级缓存是必须是代理对象实例,代码继续往下走读:
doCreateBean方法的最后从缓存中获取beanA的实例,由于第二级缓存中包含了beanA的实例,故从第二级缓存中取出beanA的实例赋值给exposedObject,这里beanA实例是代理对象。
最后beanA也存储到一级缓存中,至此beanA和beanB实例化完成。
这里思考一个问题,如果只用二级缓存是否可以解决循环依赖问题,对spring的代码进行改造,在加入到三级缓存之后立即生成实例对象并放入到二级缓存中,相当于三级缓存失效
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
getSingleton(beanName, true);
}
经过测试,spring启动速度并没有受到影响,那为什么spring中需要有三级缓存呢?
这是由于如果存在循环依赖并且某个bean存在aop代理的情况,则必须要提前创建好该bean的代理对象,而spring无法提前知道是否存在循环依赖, 为了兼容这种情况,则必须对所有bean对象都提前创建好代理对象,而这违背了spring bean的创建原则。spring是在bean创建完成之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器完成,其中的postProcessAfterInstantiation方法对初始化完成的bean完成AOP代理。我们来看下一个没有循环依赖但开启了aop代理的bean初始化情况,这里去除beanB中的beanA依赖
这里虽然将工厂对象加入到三级缓存中,但是后面并未使用到该工厂对象
执行完populateBean之后,beanA中的beanB初始化完成,此时beanA是原始对象
执行完initializeBean方法之后,exposedObject赋值为代理对象
因此spring使用第三级缓存的目的在于处理循环依赖中aop对象情景,工厂对象起到了延迟加载代理对象的作用:若只有aop没有循环依赖,则第三级缓存不会起作用,在bean创建完成之后再进行代理对象的创建;如果即有aop又有循环依赖,则通过工厂对象获取代理对象再加入到第二级缓存中