Spring源码分析之循环依赖

1.什么是循环依赖

在Java中,我们可以使用如下方式,使得两个Service互相持有,代码如下:

 

typescript

代码解读

复制代码

@Component public class AService { private BService bService; @Autowired public void setBService(BService bService) { this.bService = bService; } } @Component public class BService { private AService aService; @Autowired public void setAService(AService aService) { this.aService = aService; } } public class Logic { public static void main(String[] args) { AService aService = new AService(); BService bService = new BService(); aService.setBService(bService); bService.setAService(aService); } }

但Logic#main()这种情况并不会由于循环依赖而导致死循环等问题,循环依赖的问题主要在依赖注入(DI)框架如Spring中出现,为什么这么说呢?
通过Bean的生命周期,我们知道Bean的创建有BeanDefinition加载、Bean实例化、属性填充(依赖注入)、初始化的过程,在BeanDefinition加载、Bean实例化过程中并不会有问题,关键在于属性填充(依赖注入)这步,这步完成了注入点的值注入。值注入前需要先找到要注入的Bean,并将要注入的Bean完成创建流程,在这个过程中,如果Bean之间存在循环依赖且没有其他处理,就会导致死循环的问题,具体如下图所示:

2.如何解决循环依赖

通过上面的循环依赖生成图中,我们发现要解决这个问题,对于依赖的对象是单例对象的情况下,引入一个中间缓存即可,对于依赖的对象是多例对象的情况下,无法解决。接下来进行两种情况的分析。

2.1 单例对象

2.1.1 依赖单例普通对象

对于依赖的对象是单例对象的情况下,引入的中间缓存先将其称之为earlySingletonObjects,即将创建的AService实例(半成品)保存到中间缓存earlySingletonObjects中,当创建BService实例进行依赖注入的过程中,将AService实例(半成品)传给BService即可,如下图所示:

 通过中间缓存earlySingletonObjects,打破了循环依赖中创建依赖对象时还需重新创建原对象的问题,使得循环依赖得到解决。

2.1.2 依赖单例代理对象

通过上述方式,解决了原始对象循环依赖注入的问题,但Bean如果需要被代理,在创建过程的初始化后步骤中会创建对应代理对象返回给外部使用,因此上述方式过程给BService赋值的AService对象,与最终创建的AService并不是一个,会导致逻辑执行不一致。要解决这个问题有两种方法解决:
方法①:提前完成AService代理对象的创建并放到中间缓存中;
方法②:提供一个Bean工厂,用于创建AService代理对象。
方法①的提前完成AService代理对象的创建并放到中间缓存中的方式,会使得原本Bean的实例化、属性填充(依赖注入)、初始化(初始化前、初始化、初始化后)的流程变得复杂且不清晰不易理解,即当发生循环依赖时,会变成实例化、初始化后(创建代理对象)、属性填充(依赖注入)、初始化(初始化前、初始化、初始化后)的流程,因此此方法并不合适。
方法②的提供一个Bean工厂,用于创建AService代理对象,即在AService对象实例化后,提供一个Bean工厂,存放到中间缓存中,先将其称之为singletonFactories,接着执行到属性填充(依赖注入)步骤的创建BService过程中,通过从中间缓存singletonFactories中取得Bean工厂并执行创建对象的方法,从而得到AService对象的代理对象,完成BService的属性填充(依赖注入)步骤,进而完成BService对象的创建,使得循环依赖得到解决。整体步骤如下图所示:

 通过earlySingletonObjects、singletonFactories两个中间缓存,分别解决了基础对象循环依赖、代理对象循环依赖的问题,但实际使用过程中,这两种情况是并存的,因此需要一套流程,将两种情况兼容。

2.1.3 同时依赖普通/代理对象

从代理对象循环依赖流程中我们发现Bean工厂是用于创建代理对象的,得到的结果也是一个Bean,而普通对象循环依赖流程中中间缓存earlySingletonObjects存放的也是对象,因此将singletonFactories作为earlySingletonObjects的底层缓存即可得到兼容两种情况的流程,具体如下图所示:

整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

 需要全套面试笔记的【点击此处即可】即可免费获取

2.2 多例对象

如果AService和BService均是多例对象,意味着Bean的属性填充(依赖注入)步骤发现依赖对象未创建时,必须调用对应创建方法,完成对象创建后进行注入才行,而新创建的对象不能用于下次依赖注入,也就无法存入缓存中进行复用,因此无法解决循环依赖中依赖多例对象的情况。

2.3 其他无法解决的循环依赖情况

我们知道依赖注入的方式有:

  • 构造器注入
  • 属性注入
  • 字段注入
  • 方法参数注入

其中Bean的实例化需要执行构造方法才能完成,且实例化在属性填充(依赖注入)之前,因此构造器注入上的循环依赖无法解决,其余集中情况均可在Bean实例化后的属性填充(依赖注入)环节完成,因此可以解决。

3.源码分析

通过上述过程,我们分析了循环依赖的解决方法,接下来将深入Spring源码,看看Spring是如何解决的,源码基于Spring5.3.37版本进行分析。

3.1 三级缓存

通过Bean的生命周期我们知道了AbstractBeanFactory#doGetBean()完成了Bean的获取(创建),因此从这个方法作为入口进行分析,其中关于缓存相关源码如下:

 

java

代码解读

复制代码

protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // 省略部分代码... // 先从缓存中尝试获取单例Bean,获取的Bean有可能是完整Bean,也可能是Bean(半成品) Object sharedInstance = getSingleton(beanName); // 省略部分Bean创建相关的代码... }

我们可以看到在创建Bean之前,调用AbstractBeanFactory#getSingleton(String beanName)方法从缓存中尝试获取单例Bean,该方法内部调用了AbstractBeanFactory#getSingleton(String beanName, boolean allowEarlyReference)方法,该方法的源码如下:

 

java

代码解读

复制代码

protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 先从singletonObjects缓存中尝试获取完整的Bean对象 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 如果当前Bean处于正在创建中,则尝试从earlySingletonObjects缓存中获取Bean对象 singletonObject = this.earlySingletonObjects.get(beanName); // 若获取的依旧是空,且支持创建早期引用,则尝试从singletonFactories缓存中获取Bean对象 if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // 再次尝试从singletonObjects缓存中尝试获取完整的Bean对象,因为可能存在其他线程创建完了该对象 singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // 再次尝试从earlySingletonObjects缓存中获取Bean对象 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { // 获取Bean工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 调用Bean工厂的getObject()方法获取Bean对象 singletonObject = singletonFactory.getObject(); // 将获取的Bean对象放到earlySingletonObjects缓存中 this.earlySingletonObjects.put(beanName, singletonObject); // 将当前Bean对应的Bean工厂从singletonFactories缓存中移除 this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }

从此处,我们发现了三个缓存,分别是:singletonObjects、earlySingletonObjects、singletonFactories,三者之间是递进关系,我们将其称之为三级缓存。其中earlySingletonObjects、singletonFactories与第二节分析解决循环依赖引入缓存一样,分别存放的是Bean对象(半成品)、Bean工厂,此处我们发现earlySingletonObjects的Bean对象(半成品)来自于从singletonFactories的Bean工厂创建的Bean对象,因此若singletonFactories不存在对应Bean的Bean工厂,则earlySingletonObjects也将没有Bean对象(半成品);singletonObjects则是存放的最终完整版Bean对象,供后续使用。

3.2 singletonFactories

singletonFactories是在AbstractAutowireCapableBeanFactory#doCreateBean()方法中添加的Bean工厂,此方法作为Bean对象创建的入口方法,包含了Bean实例的创建、属性填充(依赖注入)、初始化步骤,这些均在《Spring源码之Bean的生命周期》中分析过,因此此处重点分析singletonFactories的值的来源,其核心源码如下:

 

java

代码解读

复制代码

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 省略部分代码... if (instanceWrapper == null) { // 创建Bean实例 instanceWrapper = createBeanInstance(beanName, mbd, args); } // 省略部分代码... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 省略日志代码... // 如果当前Bean是单例Bean且配置了允许循环依赖,且当前Bean正在创建中, // Bean正在创建中,意味着Bean创建了一半,重新调用了Bean的创建方法来创建依赖对象 // 则将包含当前Bean对象(半成品)BeanFactory匿名对象放到singletonFactories缓存中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { // 属性填充(依赖注入) populateBean(beanName, mbd, instanceWrapper); // 初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } // 省略部分代码... if (earlySingletonExposure) { // 如果当前Bean是单例Bean且配置了允许循环依赖,且当前Bean正在创建中,再次尝试获取Bean对象 // 此时可以从earlySingletonObjects中获取的Bean对象(半成品),因为在创建依赖对象过程中执行了BeanFactory Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 此处大部分情况下是相等,即使存在创建代理对象的过程, // 因为在创建依赖对象时创建了当前Bean的代理对象, // AbstractAutoProxyCreator#postProcessAfterInitialization()方法中将会将原始Bean对象直接返回 if (exposedObject == bean) { // 因为发生循环依赖时,代理对象放到了earlySingletonObjects中, // 所以需要再次赋值,将代理对象赋值给exposedObject并返回给上层方法 exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { // 省略抛出异常代码... } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }

本方法总结下来就是:如果当前Bean是单例Bean且配置了允许循环依赖,且当前Bean正在创建中,则将包含当前Bean对象(半成品)BeanFactory匿名对象放到singletonFactories缓存中。如果存在创建代理对象的过程,则在Bean初始化完成后,返回前,需要将代理对象(完整)从earlySingletonObjects取出并返回。此处还引入了singletonsCurrentlyInCreation缓存,用于存储创建中的Bean名称,便于Bean创建流程中的相关判断。
关于() -> getEarlyBeanReference(beanName, mbd, bean)部分对应的核心源码如下:

 

java

代码解读

复制代码

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject; }

其内部实际调用的是AbstractAutoProxyCreator#getEarlyBeanReference()方法,其核心源码如下:

 

java

代码解读

复制代码

public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 将当前对象放到earlyProxyReferences缓存中, // 用于后续AbstractAutoProxyCreator#postProcessAfterInitialization()避免再次生成代理对象 this.earlyProxyReferences.put(cacheKey, bean); // 创建代理对象 return wrapIfNecessary(bean, beanName, cacheKey); }

其中关于创建代理对象(AOP)的核心AbstractAutoProxyCreator#postProcessAfterInitialization()将于后续进行深入分析。其内部通过earlyProxyReferences缓存避免了代理对象被提前创建后的再次创建,其核心源码如下:

 

java

代码解读

复制代码

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 如果earlyProxyReferences中不存在当前Bean,才会走代理对象创建过程 if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }

3.3 singletonObjects

通过AbstractAutowireCapableBeanFactory#doCreateBean()完成了Bean的完整创建,得到了完整的Bean对象,此时需要将完整的Bean对象放置到singletonObjects缓存中,对应逻辑在DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法中,其核心源码如下:

 

java

代码解读

复制代码

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { // 首先尝试从singletonObjects缓存中获取Bean对象 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // 省略部分代码... // 此处为调用AbstractAutowireCapableBeanFactory#createBean()得到Bean对象 singletonObject = singletonFactory.getObject(); newSingleton = true; } // 省略部分代码... if (newSingleton) { // 添加到singletonObjects缓存中 addSingleton(beanName, singletonObject); } } return singletonObject; } }

4.总结

在Spring中,通过引入singletonsCurrentlyInCreation、singletonObjects、earlySingletonObjects、singletonFactories、earlyProxyReferences缓存,解决普通/代理对象的循环依赖问题。无法解决的循环依赖情况有:①构造方法注入;②依赖与被依赖的Bean均为多例Bean。

singletonsCurrentlyInCreation

在DefaultSingletonBeanRegistry类中定义,类型为Set,存放了正在创建的Bean名称,用于循环依赖过程中判断Bean是否正在创建中。

singletonObjects

在DefaultSingletonBeanRegistry类中定义,类型为Map<String, Object>,作为一级缓存,存放了创建完成(经过实例化、依赖注入、初始化)的完整Bean对象(普通或代理对象),用于后续其他Bean创建获取依赖及运行过程中获取要使用的单例Bean。

earlySingletonObjects

在DefaultSingletonBeanRegistry类中定义,类型为Map<String, Object>,作为二级缓存,存放了半成品的Bean对象(经过实例化),若不存在循环依赖则此缓存为空。当对应Bean对象(半成品)经过依赖注入、初始化流程后,将自动变成完整的Bean对象(Java对象引用的作用)。

singletonFactories

在DefaultSingletonBeanRegistry类中定义,类型为Map<String, ObjectFactory<?>>,作为三级缓存,存放了BeanFactory,在需要动态代理的情况下为原始对象生成代理对象并返回,否则返回原始对象。用于延迟代理对象的创建。此缓存作为兼容普通/代理对象获取逻辑的缓存,避免了对创建代理对象(Spring的Bean生命周期中AOP(初始化后))的流程调整,保证了Bean生命周期的清晰与完整。

earlyProxyReferences

在AbstractAutoProxyCreator类中定义,类型为Map<Object, Object>,存放了提前创建了代理对象的原始普通Bean对象,该缓存在Bean生命周期的初始化后阶段中的AbstractAutoProxyCreator#postProcessAfterInitialization()方法中被移除,用于避免初始化后的AOP流程中重复创建代理对象。

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值