Spring源码分析之循环依赖及解决方案

Spring源码分析循环依赖解决方案

往期文章:

Spring源码分析之预启动流程

Spring源码分析之BeanFactory体系结构

Spring源码分析之BeanFactoryPostProcessor调用过程详解

Spring源码分析之Bean的创建过程详解

正文:

首先,我们需要明白什么是循环依赖?简单来说就是A对象创建过程中需要依赖B对象,而B对象创建过程中同样也需要A对象,所以A创建时需要先去把B创建出来,但B创建时又要先把A创建出来...死循环有木有...

循环依赖

那么在Spring中,有多少种循环依赖的情况呢?大部分人只知道两个普通的Bean之间的循环依赖,而Spring中其实存在三种对象(普通Bean,工厂Bean,代理对象),他们之间都会存在循环依赖,这里我给列举出来,大致分别以下几种:

普通Bean与普通Bean之间

普通Bean与代理对象之间

代理对象与代理对象之间

普通Bean与工厂Bean之间

工厂Bean与工厂Bean之间

工厂Bean与代理对象之间

那么,在Spring中是如何解决这个问题的呢?

1. 普通Bean与普通Bean

首先,我们先设想一下,如果让我们自己来编码,我们会如何解决这个问题?

栗子

现在我们有两个互相依赖的对象A和B

public class NormalBeanA { private NormalBeanB normalBeanB; public void setNormalBeanB(NormalBeanB normalBeanB) { this.normalBeanB = normalBeanB; } } public class NormalBeanB { private NormalBeanA normalBeanA; public void setNormalBeanA(NormalBeanA normalBeanA) { this.normalBeanA = normalBeanA; } }

然后我们想要让他们彼此都含有对象

public class Main { public static void main(String[] args) { // 先创建A对象 NormalBeanA normalBeanA = new NormalBeanA(); // 创建B对象 NormalBeanB normalBeanB = new NormalBeanB(); // 将A对象的引用赋给B normalBeanB.setNormalBeanA(normalBeanA); // 再将B赋给A normalBeanA.setNormalBeanB(normalBeanB); } }

发现了吗?我们并没有先创建一个完整的A对象,而是先创建了一个空壳对象(Spring中称为早期对象),将这个早期对象A先赋给了B,使得得到了一个完整的B对象,再将这个完整的B对象赋给A,从而解决了这个循环依赖问题,so easy!

那么Spring中是不是也这样做的呢?我们就来看看吧~

Spring中的解决方案

由于上一篇已经分析过Bean的创建过程了,其中的某些部分就不再细讲了

先来到创建Bean的方法

AbstractAutowireCapableBeanFactory#doCreateBean

假设此时在创建A

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){ // beanName -> A // 实例化A BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); // 是否允许暴露早期对象 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 将获取早期对象的回调方法放到三级缓存中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } }

addSingletonFactory

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { // 单例缓存池中没有该Bean if (!this.singletonObjects.containsKey(beanName)) { // 将回调函数放入三级缓存 this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }

ObjectFactory是一个函数式接口

在这里,我们发现在创建Bean时,Spring不管三七二十一,直接将一个获取早期对象的回调方法放进了一个三级缓存中,我们再来看一下回调方法的逻辑

getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; // 调用BeanPostProcessor对早期对象进行处理,在Spring的内置处理器中,并无相关的处理逻辑 // 如果开启了AOP,将引入一个AnnotationAwareAspectJAutoProxyCreator,此时将可能对Bean进行动态代理 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }

在这里,如果没有开启AOP,或者该对象不需要动态代理,会直接返回原对象

此时,已经将A的早期对象缓存起来了,接下来在填充属性时会发生什么呢?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjfgf.html