简介
Spring在初始化容器过程中会对容器内的单例Bean对象进行初始化,而这初始化过程中存在对Bean的依赖注入。当存在A依赖B,B依赖C,C依赖A这种情况的时候,即出现了循环依赖的问题,这个时候Spring是如何来解决的呢?所以本文跟大家一起讨论Spring容器启动过程中对bean循环依赖的解决之道。
分析-入口
本文源码分析基于SpringFrameWork-5.1.5_release版本。因为spring的循环依赖问题是在容器初始化过程中对单例bean的初始化发生的,所以这里开始从spring对单例bean的初始化开始分析,进入org.springframework.context.support.AbstractApplicationContext类中找到refresh这个这个方法
org.springframework.context.support.AbstractApplicationContext#refresh
public void refresh() throws BeansException, IllegalStateException {
......
try {
.....
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
......
} finally {
......
}
}
}
这段代码中的this.finishBeanFactoryInitialization(beanFactory)这行代码是spring bean初始化的入口,跟踪进入这个方法
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
......
beanFactory.preInstantiateSingletons();
}
可以看到preInstantiateSingletons这个方法,这个方法是spring开始正式对容器中的单例bean进行初始化并且依赖注入。
分析-过程
跟踪进入上面这个preInstantiateSingletons方法中,来到这个org.springframework.beans.factory.support.DefaultListableBeanFactory类,方法代码如下:
public void preInstantiateSingletons() throws BeansException {
......
List<String> beanNames = new ArrayList(this.beanDefinitionNames);
Iterator var2 = beanNames.iterator();
while(true) {
String beanName;
Object bean;
do {
while(true) {
RootBeanDefinition bd;
do {
do {
do {
......
beanName = (String)var2.next();
bd = this.getMergedLocalBeanDefinition(beanName);
} while(bd.isAbstract());
} while(!bd.isSingleton());
} while(bd.isLazyInit());
if (this.isFactoryBean(beanName)) {
bean = this.getBean("&" + beanName);
break;
}
this.getBean(beanName);
}
} while(!(bean instanceof FactoryBean));
......
}
}
这里需要说明的地方是代码中存在的三个do while循环,spring想表达的逻辑是当bean定义是懒加载,不是单例或者是抽象bean的情况下,会循环下去。言外之意是当一个bean,既不是懒加载,并且是单例以及不是抽象的情况下,会跳出循环从而进入到getBean方法中进行bean的初始化。
跟踪进入getBean方法,最终会进入到org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中,需要关注的代码如下:
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = this.transformedBeanName(name);
//关注点1,先尝试获取bean
Object sharedInstance = this.getSingleton(beanName);
Object bean;
//关注点2,如果sharedInstance不为空,则直接返回。
if (sharedInstance != null && args == null) {
if (this.logger.isTraceEnabled()) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
} else {
//关注点3,这里的意思是当需要从Spring容器中获取Prototype类型的bean时,而这个bean处于正在创建的阶段时会直接抛出异常,言外之意即对于Prototype类型的bean,spring不支持循环依赖。
if (this.isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
......
try {
RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(mbd, beanName, args);
......
if (mbd.isSingleton()) {
//关注点4,单例bean的初始化入口
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
......
} catch (BeansException var26) {
this.cleanupAfterBeanCreationFailure(beanName);
throw var26;
}
}
......
}
从上面的标注的关注点1中可以看到,获取bean的时候会先尝试从spring容器中获取bean,进入关注1中的方法,最终可以跟踪进入到org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)中,代码如下:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先从singletonObjects这个map缓存中获取已经初始化完毕的对象
Object singletonObject = this.singletonObjects.get(beanName);
//如果获取不到,要不就说明这个bean对象还没有开始创建,要不就处于正在创建阶段,比如,A依赖B,B依赖A,spring在初始化B对象的时候,会依赖注入,获取依赖A对象,而这个A对象正好处于正在创建阶段,所以会进入到这个判断
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
Map var4 = this.singletonObjects;
synchronized(this.singletonObjects) {
//earlySingletonObjects这个也是一个map缓存对象,预加载完成但没有进行依赖注入的bean对象会在这个里面
singletonObject = this.earlySingletonObjects.get(beanName);
//如果earlySingletonObjects里获取不到,并且允许获取bean早期引用
if (singletonObject == null && allowEarlyReference) {
//重点关注,singletonFactories这个也是一个key为beanName,value为ObjectFactory的map缓存对象(通过这个ObjectFactory的getObject方法可以获取早期应用对象)
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
代码逻辑需要关注的地方这里使用了注解说明,其中需要理解的点有几个,第一个是singletonObjects,earlySingletonObjects,singletonFactories这几个缓存对象的作用,第二个是singletonFactories这个对象的key,value是在什么时候在put填充的(后面说明)。
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
private final Map<String, Object> earlySingletonObjects = new HashMap(16);
从上面的源码中可以看出singletonObjects,singletonFactories,earlySingletonObjects都是map对象,分别用于缓存已经初始化完毕(包括依赖注入)的bean,bean对象的ObjectFactory对象(用于获取早期bean对象)以及早期应用bean对象(仅仅缓存作用)。
重新回到关注点4代码中,代码如下:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
跟踪进入org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)方法中,最终会触发this.createBean(beanName, mbd, args)的调用,进行bean的初始化,代码如下:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
Map var3 = this.singletonObjects;
synchronized(this.singletonObjects) {
//先从singletonObjects缓存中获取bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//如果当前获取的bean处于正在销毁的阶段,spring直接抛出异常,即无法获取正在处于销毁阶段的bean。
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
......
//这里是将beanName加入到singletonsCurrentlyInCreation缓存列表中,用于判断当前beanName是否处于初始化阶段
this.beforeSingletonCreation(beanName);
boolean newSingleton = false;
......
try {
//调用方法参数singletonFactory的getObject,即触发createBean方法初始化bean对象
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException var16) {
......
} catch (BeanCreationException var17) {
......
} finally {
......
//从singletonsCurrentlyInCreation缓存列表中移除当前创建的bean,表明该bean对象已经不处于正在初始化阶段
this.afterSingletonCreation(beanName);
}
if (newSingleton) {
//将成功初始化的bean对象加入singletonObjects缓存map中,并且singletonFactories,earlySingletonObjects对象中移除对应beanName的早期引用获取方法,以及早期引用。
this.addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
上面的源码逻辑从注解中可以看出,其中创建bean的操作会进入createBean方法,最终可以跟踪进入org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,代码如下:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
......
//bean实例化
if (instanceWrapper == null) {
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
......
//bean是单例的,并且允许循环依赖以及当前bean处于正在创建的阶段
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
//重点关注,将早期引用获取的ObjectFactory类put进入singletonFactories缓存Map对象中
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
Object exposedObject = bean;
try {
//依赖注入核心
this.populateBean(beanName, mbd, instanceWrapper);
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
......
}
......
try {
......
return exposedObject;
} catch (BeanDefinitionValidationException var16) {
......
}
}
至此,Spring通过对singletonFactories,earlySingletonObjects,singletonObjects几个缓存对象的使用,解决了单例bean循环依赖的问题。需要注意的是,这里的依赖指的是属性依赖,如果是构造器依赖或者是Prototype类型的bean,spring是不支持的,原因主要是spring通过构造器实例化bean对象的时候,还未调用addSingletonFactory这个方法,所以无法提前获取到依赖bena的预初始化的bean对象,而Prototype类型的bean在获取依赖的时候会判断是否处于正在创建阶段,如果是会直接抛出异常。所以总结来说Spring在5.1.5这个版本中仅支持单例bean的循环依赖,其他版本可以参考本文源码分析自行了解。
分析-总结
总结下,在Spring中,单例A依赖B,B依赖A中,首先Spring先进入A的创建,在通过构造方法实例化后,因为bean是单例的,并且允许循环依赖以及当前bean处于正在创建的阶段,所以spring会将早期引用获取的ObjectFactory类put进入singletonFactories缓存Map对象中,后面开始
populateBean进行依赖注入,会解析得到A的依赖B,从而进入B对象的初始化,当进行B对象的初始化过程中,同样也会进行B对象的依赖注入,会解析得到B的依赖A,从而进入A对象的获取即doGetBean操作,doGetBean中会先调用getSingleton(org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean))方法尝试获取初始化完毕的bena对象或者是预初始化的bean对象,从而最终B获取到了预初始化的A对象(已经实例化但还没进行依赖注入)。
所以平时代码中如果在bean创建出现BeanCurrentlyInCreationException或者BeanCreationException异常时,可以先考虑下是否bean依赖出现循环依赖问题,结合本文的内容进行排查。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。