spring源码分析之如何解决循环依赖

spring源码分析之如何解决循环依赖

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账号...

(0)
blank

相关推荐

  • 微信电脑版打不开怎么办?电脑版微信打不开的解决方案_wechatwin.dll文件缺失怎么解决

    微信电脑版打不开怎么办?电脑版微信打不开的解决方案_wechatwin.dll文件缺失怎么解决微信现在除了是日常的交流工具,基本上办公也离不开它,微信也注意到大家的意愿,所以也开发了电脑端的微信,不过有时候电脑版的微信也不好用,遇到紧急情况需要通知同事的时候又发现打不开了,通常遇上这种情况,我们要采取这些措施。解决办法如下:电脑版微信打不开是怎么一回事?第一步要做的就是,排查网络或是电脑系统的问题,可以重启试试。排除电脑或者网络问题以后,那可能是微信客户端不稳定。也许是文件损坏了或者是系统…

  • 英文高频词

    英文高频词(‘rods’,100)(‘carbohydrates’,100)(‘stresses’,100)(‘premiered’,100)(‘cyrus’,100)(‘drastically’,100)(‘eukaryotic’,100)(‘witchcraft’,100)(‘accompaniment’,100)(‘conventionally’,

  • 你不知道的JavaScript(上中下三册) 电子版高清PDF — 百度云下载

    你不知道的JavaScript(上中下三册) 电子版高清PDF — 百度云下载链接:https://pan.baidu.com/s/1pfviFDVDg7EpF35HQWEc6Q密码:vaft 

  • CPU型号后缀字母所代表的含义

    CPU型号后缀字母所代表的含义一、Intel桌面式CPU——只看数字你就输了●X后缀X后缀=至高无上的至尊版  X代表Extreme,中文意思是至尊级,代表同一时代性能最强的CPU。如Corei7-5960X、Corei7-4960X。X代表在同一代中只有一款CPU黄袍加身,地位至高无上。加上没有竞争对手可以望其项背,…

  • 解决TimeWait过多的问题

    解决TimeWait过多的问题原文链接:https://www.tiejiang.org/27040.html1、time_wait的作用:TIME_WAIT状态存在的理由:1)可靠地实现TCP全双工连接的终止在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,因此客户端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connectionreset的SocketExc

  • 关系数据库模型设计「建议收藏」

    关系数据库模型设计「建议收藏」本文从现实世界-概念世界(信息世界)-机器世界(数据世界)逐级抽象,旨在以浅显易懂的语言描述关系数据库应该如何建模,最后用简单名了的描述给出关系模型的设计范式的含义。

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号