本文现已收录到Github库房,该库房包括计算机根底、Java核心知识点、多线程、JVM、常见结构、分布式、微服务、设计模式、架构等核心知识点,欢迎star~

Github地址:github.com/Tyson0314/J…

Gitee地址:gitee.com/tysondai/Ja…

前语

在运用spring结构的日常开发中,bean之间的循环依靠太频频了,spring现已帮咱们去处理循环依靠问题,对咱们开发者来说是无感知的,下面具体剖析一下spring是怎样处理bean之间循环依靠,为什么要运用到三级缓存,而不是二级缓存?

bean生命周期

首要大家需求了解一下bean在spring中的生命周期,bean在spring的加载流程,才能够愈加清晰知道spring是怎样处理循环依靠的。

Spring 为何需要三级缓存解决循环依赖?

咱们在spring的BeanFactory工厂列举了许多接口,代表着bean的生命周期,咱们首要记住的是我圈红线圈出来的接口, 再结合spring的源码来看这些接口首要是在哪里调用的

Spring 为何需要三级缓存解决循环依赖?

AbstractAutowireCapableBeanFactory类的doCreateBean办法是创立bean的开始,咱们能够看到首要需求实例化这个bean,也便是在堆中开辟一块内存空间给这个目标,createBeanInstance办法里面逻辑大约便是选用反射生成实例目标,进行到这儿表明目标还并未进行特点的填充,也便是@Autowired注解的特点还未得到注入

Spring 为何需要三级缓存解决循环依赖?

咱们能够看到第二步便是填充bean的成员特点,populateBean办法里面的逻辑大致便是对运用到了注入特点的注解就会进行注入,假如在注入的过程发现注入的目标还没生成,则会跑去生产要注入的目标,第三步便是调用initializeBean办法初始化bean,也便是调用咱们上述所说到的接口

Spring 为何需要三级缓存解决循环依赖?

能够看到initializeBean办法中,首要调用的是运用的Aware接口的办法,咱们具体看一下invokeAwareMethods办法中会调用Aware接口的那些办法

Spring 为何需要三级缓存解决循环依赖?

咱们能够知道假如咱们完结了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware三个Aware接口的话,会顺次调用setBeanName(), setBeanClassLoader(), setBeanFactory()办法,再看applyBeanPostProcessorsBeforeInitialization源码

Spring 为何需要三级缓存解决循环依赖?

发现会假如有类完结了BeanPostProcessor接口,就会履行postProcessBeforeInitialization办法,这儿需求留意的是:假如多个类完结BeanPostProcessor接口,那么多个完结类都会履行postProcessBeforeInitialization办法,能够看到是for循环顺次履行的,还有一个留意的点便是假如加载A类到spring容器中,A类也重写了BeanPostProcessor接口的postProcessBeforeInitialization办法,这时要留意A类的postProcessBeforeInitialization办法并不会得到履行,由于A类还未加载完结,还未完全放到spring的singletonObjects一级缓存中。

再看一个留意的点

Spring 为何需要三级缓存解决循环依赖?

Spring 为何需要三级缓存解决循环依赖?

能够看到ApplicationContextAwareProcessor也完结了BeanPostProcessor接口,重写了postProcessBeforeInitialization办法,办法里面并调用了invokeAwareInterfaces办法,而invokeAwareInterfaces办法也写着假如完结了众多的Aware接口,则会顺次履行相应的办法,值得留意的是ApplicationContextAware接口的setApplicationContext办法,再看一下invokeInitMethods源码

Spring 为何需要三级缓存解决循环依赖?

发现假如完结了InitializingBean接口,重写了afterPropertiesSet办法,则会调用afterPropertiesSet办法,最终还会调用是否指定了init-method,能够通过标签,或者@Bean注解的initMethod指定,最终再看一张applyBeanPostProcessorsAfterInitialization源码图

Spring 为何需要三级缓存解决循环依赖?

发现跟之前的postProcessBeforeInitialization办法类似,也是循环遍历完结了BeanPostProcessor的接口完结类,履行postProcessAfterInitialization办法。整个bean的生命履行流程就如上面截图所示,哪个接口的办法在哪里被调用,办法的履行流程。

最终,对bean的生命流程进行一个流程图的总结

Spring 为何需要三级缓存解决循环依赖?

三级缓存处理循环依靠

上一小节对bean的生命周期做了一个整体的流程剖析,对spring怎样去处理循环依靠的很有协助。前面咱们剖析到填充特点时,假如发现特点还未在spring中生成,则会跑去生成特点目标实例。

Spring 为何需要三级缓存解决循环依赖?

咱们能够看到填充特点的时分,spring会提早将现已实例化的bean通过ObjectFactory半成品露出出去,为什么称为半成品是由于这时分的bean目标实例化,但是未进行特点填充,是一个不完好的bean实例目标

Spring 为何需要三级缓存解决循环依赖?

spring利用singletonObjects, earlySingletonObjects, singletonFactories三级缓存去处理的,所说的缓存其实也便是三个Map

Spring 为何需要三级缓存解决循环依赖?

能够看到三级缓存各自保存的目标,这儿重点关注二级缓存earlySingletonObjects和三级缓存singletonFactory,一级缓存能够进行疏忽。前面咱们讲过先实例化的bean会通过ObjectFactory半成品提早露出在三级缓存中

Spring 为何需要三级缓存解决循环依赖?

singletonFactory是传入的一个匿名内部类,调用ObjectFactory.getObject()最终会调用getEarlyBeanReference办法。再来看看循环依靠中是怎样拿其它半成品的实例目标的。

咱们假设现在有这样的场景AService依靠BService,BService依靠AService

\1. AService首要实例化,实例化通过ObjectFactory半成品露出在三级缓存中

\2. 填充特点BService,发现BService还未进行过加载,就会先去加载BService

\3. 再加载BService的过程中,实例化,也通过ObjectFactory半成品露出在三级缓存

\4. 填充特点AService的时分,这时分能够从三级缓存中拿到半成品的ObjectFactory

Spring 为何需要三级缓存解决循环依赖?

拿到ObjectFactory目标后,调用ObjectFactory.getObject()办法最终会调用getEarlyBeanReference()办法,getEarlyBeanReference这个办法首要逻辑大约描绘下假如bean被AOP切面署理则回来的是beanProxy目标,假如未被署理则回来的是原bean实例。

这时咱们会发现能够拿到bean实例(特点未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时B注入的是一个半成品的实例A目标,不过随着B初始化完结后,A会继续进行后续的初始化操作,最终B会注入的是一个完好的A实例,由于在内存中它们是同一个目标。

下面是重点,咱们发现这个二级缓存如同显得有点剩余,如同能够去掉,只需求一级和三级缓存也能够做到处理循环依靠的问题???

只需两个缓存的确能够做到处理循环依靠的问题,但是有一个前提这个bean没被AOP进行切面署理,假如这个bean被AOP进行了切面署理,那么只运用两个缓存是无法处理问题,下面来看一下bean被AOP进行了切面署理的场景

Spring 为何需要三级缓存解决循环依赖?

咱们发现AService的testAopProxy被AOP署理了,看看传入的匿名内部类的getEarlyBeanReference回来的是什么目标。

Spring 为何需要三级缓存解决循环依赖?

发现singletonFactory.getObject()回来的是一个AService的署理目标,仍是被CGLIB署理的。再看一张再履行一遍singletonFactory.getObject()回来的是否是同一个AService的署理目标

Spring 为何需要三级缓存解决循环依赖?

咱们会发现再履行一遍singleFactory.getObject()办法又是一个新的署理目标,这就会有问题了,由于AService是单例的,每次履行singleFactory.getObject()办法又会发生新的署理目标。

假设这儿只要一级和三级缓存的话,我每次从三级缓存中拿到singleFactory目标,履行getObject()办法又会发生新的署理目标,这是不行的,由于AService是单例的,所有这儿咱们要借助二级缓存来处理这个问题,将履行了singleFactory.getObject()发生的目标放到二级缓存中去,后边去二级缓存中拿,没必要再履行一遍singletonFactory.getObject()办法再发生一个新的署理目标,确保始终只要一个署理目标。还有一个留意的点

Spring 为何需要三级缓存解决循环依赖?

已然singleFactory.getObject()回来的是署理目标,那么注入的也应该是署理目标,咱们能够看到注入的的确是通过CGLIB署理的AService目标。所以假如没有AOP的话的确能够两级缓存就能够处理循环依靠的问题,假如加上AOP,两级缓存是无法处理的,不可能每次履行singleFactory.getObject()办法都给我发生一个新的署理目标,所以还要借助另外一个缓存来保存发生的署理目标

总结

前面先讲到bean的加载流程,了解了bean加载流程对spring怎样处理循环依靠的问题很有协助,后边再剖析到spring为什么需求利用到三级缓存处理循环依靠问题,而不是二级缓存。网上能够试试AOP的情形,实践一下就能明白二级缓存为什么处理不了AOP署理的场景了

在工作中,一向以为编程代码不是最重要的,重要的是在工作中所养成的编程思维。

原文:cnblogs.com/semi-sub/p/13548479.html