200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Spring源码分析之如何解决循环依赖

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

时间:2021-10-21 15:26:12

相关推荐

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

spring-ioc中循环依赖的问题,也算是高频的面试问题了,今天跟大家一起来总结一下spring-ioc中是如何解决循环依赖的,相信大家是可以从这篇文章中彻底理解spring容器如何帮我们解决循环依赖,为了更好的理解spring-ioc如何解决循环依赖,大家可以先简单的了解spring-ioc中bean实例化的整个时序图。

一、spring-ioc解决循环依赖的位置

红色的标注框的地方,表示解决循环依赖的重点逻辑,后面会跟大家一起详细阅读,这里大家可以先有个印象

二、spring-ioc解决循环依赖的动态示意图

首先spring-ioc是无法解决构造函数中循环依赖的问题,这个后面会一起解释。咱们先用一个简单的例子和示意图来描述spring-ioc解决循环依赖的思想,最后再去阅读源码,我相信大家能更容易理解

@Servicepublic class ACircleService {@Autowiredprivate BCircleService bCircleService;}

@Servicepublic class BCircleService {@Autowiredprivate 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包装类BeanWrapper,这段代码执行结束,说明bean已经构建完成,在堆上创建了实例//调用构造函数创建该bean对象,若不存在构造函数注入,顺利通过instanceWrapper = createBeanInstance(beanName, mbd, args);}//(2).拿到beanfinal 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)在singletonFactories缓存中,放入该bean对象,以便解决循环依赖问题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中的(4)获取B,而是在(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>(){@Overridepublic 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是如何解决循环依赖,如果回答错误,那么还需要继续看看

原文链接:spring源码分析之如何解决循环依赖_hongtaolong的博客-CSDN博客_spring中的循环依赖

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。