spring-ioc中循环依赖的问题,也算是高频的面试问题了,今天跟大家一起来总结一下spring-ioc中是如何解决循环依赖的,相信大家是可以从这篇文章中彻底理解spring容器如何帮我们解决循环依赖,为了更好的理解spring-ioc如何解决循环依赖,大家可以先简单的了解spring-ioc中bean实例化的整个时序图。
一、spring-ioc解决循环依赖的位置
红色的标注框的地方,表示解决循环依赖的重点逻辑,后面会跟大家一起详细阅读,这里大家可以先有个印象
二、spring-ioc解决循环依赖的动态示意图
首先spring-ioc是无法解决构造函数中循环依赖的问题,这个后面会一起解释。咱们先用一个简单的例子和示意图来描述spring-ioc解决循环依赖的思想,最后再去阅读源码,我相信大家能更容易理解
@Service
public class ACircleService {
@Autowired
private BCircleService bCircleService;
}
@Service
public class BCircleService {
@Autowired
private ACircleService aCircleService;
}
相信大家经常这么使用,此时ACircleService(后面简称A)引用BCircleService(后面简称B)B中引用A就构成了一个循环依赖,下面我们用示意图来描述它们的创建过程
1、首先大家理解下面几个容器
(1)singletonsCurrentlyInCreation:表示当前正在创建的bean(仅仅存放名字beanName),如果没创建完成,都会保存在这里面
(2)singletonObjects:一级缓存,可以理解为bean最终都是放在这里面的,一个bean真正完成了就放到这里面
(3)earlySingletonObjects:二级缓存,过渡使用
(4)singletonFactories:三级缓存,也是过渡使用(提前曝光的bean存放),它与二级缓存不同的是它放的是ObjectFactory,而不是最终的Bean,二级缓存中是放的三级缓存getObject的结果
介绍完上面的容器,我们接着看在创建A,B时它是如何从上述的容器中变化
2、变化过程图
假如先创建A,此时四个容器中都是空的
(1)先依次从一级、二级、三级缓存中判断是否有能拿到A,结果显然是拿不到,四个容器都是空的,我就不画了
(2)要开始创建A,此时需要往singletonsCurrentlyInCreation放入A,表示A正在实例化,此时四个容器的状态如下
(3)接下来正式开始创建A到A创建完成(堆上面已经分配了空间,但是属性还没赋值),此时将A封装成ObjectFactory对象(为什么要封装,后面会讲一下),大家可以认为此时的A对象已经创建,但是属性未赋值,我们暂时用下面命名AObjectFactory,但是AobjectFactory.getObject() == A(A的地址假设A@9527xxx),此时A是在堆上已经创建好了,但是它的属性是null(bCircleService==null),我不知道这里有没有描述清楚,容器状态如下:
(4)此时要给A的属性赋值,这里就是给bCircleService赋值,那么就去创建B,创建B的过程和创建A的过程一样的,先依次从一级缓存、二级缓存、三级缓存中获取B,显然获取不到,那么正式开始创建B,singletonsCurrentlyInCreation中加入B,表示当前也正在创建B,容器状态如下:
(5)接下来同(3)类似,B创建完成,此时也只是在堆上创建好了对象,但是B中的属性aCircleService还没有赋值(aCircleService==null),此时将B封装成BObjectFactory放到三级缓存,容器状态如下:
(6)接下来是重点了,此时开始给B的属性赋值了,这里即给aCircleService 赋值,那么它就要去创建A,并且把A的内存地址付给B中的aCircleService属性,那么创建A的过程和之前的一样,先依次从一级、二级、三级缓存中拿A,此时是可以从三级缓存中拿到A的,那么将拿到的A赋值给B的aCircleService属性,此时aCircleService==A@9527xxx,此时B即将创建完成了,在全部创建完的前一步,将三级缓存中的B移到二级缓存(存放的是BObjectFactory.getObject()),因为实例化B的全部步骤全部做完了,此时容器的状态如下:
(7)此时B(假设地址是B@9527)已经全部实例化完成了,但是还有一些收尾的工作呀,就是需要从当前正在创建的容器(singletonsCurrentInCreation)中移除(表示B创建完成),并且同时将B移动到一级缓存singletonObjects中,此时的容器状态
(8)上述B已经创建完成了,我们要记得创建B的时机是在A给bCircleService赋值的时候,所以我们的逻辑又到了给A的属性赋值的时候了,此时我们知道B已经创建完成了,所以bCircleService==B@9527,此时A的实例化也快要结束了,A也要将三级缓存的内容移到二级缓存,类似过程(6),容器状态如下:
(9)最后A也要做一些结束的动作,类似过程(7),到这里A和B都已经全部实例化完成了
总结:上述看上去流程挺多的,其实主要的核心就是在A创建完成(对象已经在堆中分配),还没有给属性赋值(bCircleService==null)的过程中,将A封装成ObjectFactory,放到三级缓存。然后在实例化B的过程中,给B的属性aCircleService赋值时,依次 从容器中拿A,此时是可以从三级缓存中拿到,所以不会再去走创建A的过程了,相当于提前曝光了A
上面还留了两个问题,会在下面的源码分析中解释
(1)为什么spring-ioc中无法解决构造函数中的循环依赖
(2)为什么需要使用三级缓存,而且里面装的是ObjectFactory
三、源码分析
1、AbstractBeanFactory#doGetBean()
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;
// Eagerly check singleton cache for manually registered singletons.
//(1).先依次从一级、二级、三级缓存中看看能否取到
Object sharedInstance = getSingleton(beanName);
//(2).如果缓存中能取到,则不会走下面的创建
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);
}
//(3).如果缓存中取不到,则走创建的逻辑
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
//省略代码...
// Create bean instance.
if (mbd.isSingleton()) {
//(4).主要的创建逻辑
sharedInstance = getSingleton(beanName, () -> {
try {
//(5).java8函数式编程
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//省略代码...
}
//省略代码...
return (T) bean;
}
上述代码我省略了很多,主要保留了需要分析循环依赖的逻辑,上面已经加了注释
首先从一级、二级、三级缓存中取
下面的if else针对是否能从缓存中取出结果
结合上述的图,我们发现第一次创建A,显然是走else的创建逻辑getSingleton
2、DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
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!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
//(1).开始创建之前调用方法
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//(2).真正的调用方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
//(3).开始创建之后调用方法
afterSingletonCreation(beanName);
}
if (newSingleton) {
//(4).创建成功之后调用的方法
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
我在四个核心的方法上加了注释
beforeSingletonCreation(beanName);
singletonFactory.getObject();
afterSingletonCreation(beanName);
addSingleton(beanName, singletonObject);
3、DefaultSingletonBeanRegistry#beforeSingletonCreation(beanName)
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
比较简单,其实就是判断当前正在创建的容器singletonsCurrentlyInCreation是否已经包含正在创建的类,如果包含,则抛异常,我们在构造做循环依赖就会在这里抛异常,后面会具体分析为什么会在这里抛异常
4、DefaultSingletonBeanRegistry#afterSingletonCreation(beanName);
这里我们先没有分析singletonFactory.getObject()创建的核心逻辑,因为比较长一时半会儿分析不了,我们分析afterSingletonCreation是因为它和上面的beforeSingletonCreation有关联。这里我们先假设singletonFactory.getObject()已经成功执行完了,我们看afterSingletonCreation()
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
很简单,就是将singletonsCurrentlyInCreation清除当前正在创建的bean,因为此时我们已经创建完了,接下来再接着看addSingleton
5、DefaultSingletonBeanRegistry#addSingleton
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
很简单,4、5的步骤对应我们的上述第二节示意图(6)-(7)的过程。上面我们还没有分析singletonFactory.getObject()创建bean的核心逻辑,只是假设它成功调用完成了,我们现在回过头来分析
6、ObjectFactory#getObject()
这里其实是调用的AbstractAutowireCapableBeanFactory#createBean(),因为这里是使用java8的lambda表达式,传的是一个函数(参考1中代码注释(5)的那一段代码)
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//省略代码....
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//省略代码...
}
省略了代码,我们重点看doCreateBean,这个是核心
7、AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//(1).创建bean包装类BealWrapper,这段代码执行结束,说明bean已经构建完成,在堆上创建了实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//(2).拿到bean
final Object bean = instanceWrapper.getWrappedInstance();
//省略部分代码...
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//(3).这里非常重要,没有删除原来的英文注释,这里就是提前曝光
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");
}
//(3-1)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//(4).给属性赋值
populateBean(beanName, mbd, instanceWrapper);
//(5).调用初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
if (earlySingletonExposure) {
//(6).bean在容器中移动
Object earlySingletonReference = getSingleton(beanName, false);
//省略部分代码...
return exposedObject;
}
这里是解决循环依赖的核心—即提前曝光一个实例(该实例已经创建好,但是里面的属性还没赋值,因为赋值的逻辑要到代码(4),而提前曝光的逻辑在(3))。拿上面的循环依赖A,B来说,代码执行完(3)时,A对象已经在堆上分配,只是bCircleService == null而已。此时将A提前曝光,插入到三级缓存中;而实例化B的入口则在(4)中,给bCircleservice赋值,此时如果B没有创建,就开始创建B。等B同样执行完上述(3),则B也在堆上分配了,只是暂时B中的aCircleService==null,所以B执行(4)时,去创建A,此时创建A先依次从一级、二级、三级缓存中取A时是可以在三级缓存中取到,因此代码不会执行1中的else逻辑,而是执行if。
我们现在来处理上述的第一个问题:为什么构造函数中的循环依赖不能解决?
我们还是拿A,B来举例(假如A的构造函数中依赖B),如果在构造函数中循环依赖,则A不会上述代码7中的(3)而是在(1)中就去获取B(此时注意A没有提前曝光,即一级、二级、三级缓存中都不存在),假如此时B没有创建,则开始创建B。等B执行到(4)时,给B中的aCircleService赋值时,需要去创建A,先从一级、二级、三级缓存中去取A,取不到,则走代码1中的else逻辑,else的逻辑最终会调用beforeSingletonCreation(beanName),因此就抛异常了。
我们处理第二个问题,为什么需要使用三级缓存singletonFactories,里面装的是ObjectFactory,而不是直接将ObjectFactory.getObject()获取的结果放到二级缓存earlySingletonObjects呢?
这里我们就需要看上述代码7中的(3-1)addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));这里是使用java8的函数式编程,如果不明白的,我用下面的匿名内部类来替换把
addSingletonFactory(beanName, new ObjectFactory<Object>(){
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference();
}
});
我们再看看getEarlyBeanReference()方法
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;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
这里无非就是对这个bean进行拦截,做一些处理,最终这个方法的调用时机,是在代码2中的(4),此时bean的实例化的基本全部完成,所以这里起到了延迟调用的作用。如果不延迟调用存在两种情况
(1)不调用getEarlyBeanReference(),则有些实例创建没有起到必要的拦截
(2)不延迟,那么可能该实例仅仅是在堆上分配了,里面属性什么都没赋值,初始化方法也没调用,可能我们用beanpostprocessor拦截也没任何意义,达不到效果。
这里大家可以思考一下这两个问题,
1.当A构造函数中依赖B,而在B中依赖A不是构造函数依赖,会不会报错
2.当A依赖B时不是构造函数依赖,而B依赖A时是构造函数依赖,会不会报错
如果大家对这两个问题都能回答正确,我相信是彻底理解了spring是如何解决循环依赖,如果回答错误,那么还需要继续看看
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/111202.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...