通俗易懂的Spring三级缓存解决循环依赖的详细讲解,包教包会

前言

我们都知道Spring中的BeanFactory是一个IOC容器,负责创建Bean和缓存一些单例的Bean对象,以供项目运行过程中使用。

创建Bean的大概的过程:

  1. 实例化Bean对象,为Bean对象在内存中分配空间,各属性赋值为默认值
  2. 初始化Bean对象,为Bean对象填充属性
  3. 将Bean放入缓存

首先,容器为了缓存这些单例的Bean需要一个数据结构来存储,比如Map {k:name; v:bean}。

而我们创建一个Bean就可以往Map中存入一个Bean。这时候我们仅需要一个Map就可以满足创建+缓存的需求。
但是创建Bean过程中可能会遇到循环依赖问题,比如A对象依赖了一个B对象,而B对象内部又依赖了一个A,如下:

public class A {
    B b;
}
public class B {
    A a;
}

假设A和B我都定义为单例的对象,并且需要在项目启动过程中自动注入,如下:

@Component
public class A {
    @Autowired
    B b;
}

@Component
public class B {
    @Autowired
    A a;
}

一级缓存

  1. 实例化A对象。
  2. 填充A的属性阶段时需要去填充B对象,而此时B对象还没有创建,所以这里为了完成A的填充就必须要先去创建B对象;
  3. 实例化B对象。
  4. 执行到B对象的填充属性阶段,又会需要去获取A对象,而此时Map中没有A,因为A还没有创建完成,导致又需要去创建A对象。
  5. 这样,就会循环往复,一直创建下去,只到堆栈溢出。
为什么不能在实例化A之后就放入Map?

因为此时A尚未创建完整,所有属性都是默认值,并不是一个完整的对象,在执行业务时可能会抛出未知的异常。所以必须要在A创建完成之后才能放入Map。

二级缓存

此时我们引入二级缓存用另外一个Map2 {k:name; v:earlybean} 来存储尚未已经开始创建但是尚未完整创建的对象。

  1. 实例化A对象之后,将A对象放入Map2中。
  2. 在填充A的属性阶段需要去填充B对象,而此时B对象还没有创建,所以这里为了完成A的填充就必须要先去创建B对象。
  3. 创建B对象的过程中,实例化B对象之后,将B对象放入Map2中。
  4. 执行到B对象填充属性阶段,又会需要去获取A对象,而此时Map中没有A,因为A还没有创建完成,但是我们继续从Map2中拿到尚未创建完毕的A的引用赋值给a字段。这样B对象其实就已经创建完整了,尽管B.a对象是一个还未创建完成的对象。
  5. 此时将B放入Map并且从Map2中删除。
  6. 这时候B创建完成,A继续执行b的属性填充可以拿到B对象,这样A也完成了创建。
  7. 此时将A对象放入Map并从Map2中删除。
二级缓存已然解决了循环依赖问题,为什么还需要三级缓存?

从上面的流程中我们可以看到使用两级缓存可以完美解决循环依赖的问题,但是Spring中还有另外一个问题需要解决,这就是初始化过程中的AOP实现。
AOP是Spring的重要功能,实现方式就是使用代理模式动态增强类的功能。
动态单例目前有两种技术可以实现,一种是JDK自带的基于接口的动态Proxy技术,一种是CGlib基于字节码动态生成的Proxy技术,这两种技术都是需要原始对象创建完毕,之后基于原始对象生成代理对象的。

那么我们发现,在二级缓存的设计下,我们需要在放入缓存Map之前将代理对象生成好。
将流程改为:

  1. 实例化Bean对象,为Bean对象在内存中分配空间,各属性赋值为默认值
  2. 如果有动态单例,生成Bean对象的代理Proxy对象
  3. 初始化Proxy对象,为Bean对象填充属性
  4. 将Proxy放入缓存

这样虽然也可以解决,AOP的问题,但是我们知道Spring中AOP的实现是通过后置处理器BeanPostProcessor机制来实现的,而后置处理器是在填充属性结束后才执行的。流程如下:

  1. 实例化对象
  2. 对象填充属性
  3. BeanPostProcessor doBefore
  4. init-method
  5. BeanPostProcessor doAfter – AOP是在这个阶段实现的

所以要实现上面的方案,势必需要将BeanPostProcessor阶段提前或者侵入到填充属性的流程中,那么从程序设计上来说,这样做肯定是不美的。

三级缓存

Spring引入了第三级缓存来解决这个问题, Map3 {k:name v:ObjectFactory} ,这个缓存的value就不是Bean对象了,而是一个接口对象由一段lamda表达式实现。在这段lamda表达式中去完成一些BeanPostProcessor的执行。

  1. 实例化A对象之后,将A的ObjectFactory对象放入Map3中。
  2. 在填充A的属性阶段需要去填充B对象,而此时B对象还没有创建,所以这里为了完成A的填充就必须要先去创建B对象。
  3. 创建B对象的过程中,实例化B的ObjectFactory对象之后,将B对象放入Map2中。
  4. 执行到B对象填充属性阶段,又会需要去获取A对象,而此时Map1中没有A,因为A还没有创建完成,但是我们继续从Map2中也拿不到,到Map3中获取了A的ObjectFactory对象,通过ObjectFactory对象获取A的早期对象,并将这个早期对象放入Map2中,同时删除Map3中的A,将尚未创建完毕的A的引用赋值给a字段。这样B对象其实就已经创建完整了,尽管B.a对象是一个还未创建完成的对象。
  5. 此时将B放入Map并且从Map3中删除。
  6. 这时候B创建完成,A继续执行b的属性填充可以拿到B对象,这样A也完成了创建。
  7. 此时将A对象放入Map并从Map2中删除。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cidvCRtJ-1649250821901)(//upload-images.jianshu.io/upload_images/19104673-b92a5bbb58635bc4.gif?imageMogr2/auto-orient/strip|imageView2/2/w/646/format/webp)]

源码步骤解析

  1. SpringBoot项目启动执行到SpringApplication#run 中的refreshContext(context);,最终调用Spring容器的AbstractApplicationContext#refresh方法,开始初始化BeanFactory。
  2. AbstractApplicationContext#refresh步骤中,执行到AbstractApplicationContext#finishBeanFactoryInitialization方法,开始完成 Bean 工厂初始化。
  3. 执行到AbstracBeanFactory.preInstantiateSingletons(),开始根据BeanFactory中的BeanDefinition信息初始化Bean对象。
  4. AbstracBeanFactory.preInstantiateSingletons()方法中,发现A对象的BeanDefinition,执行AbstracBeanFactory.getBean方法,获取A对象。
  5. AbstracBeanFactory.getBean方法中,执行AbstracBeanFactory.doGetBean方法,获取A对象。
  6. AbstracBeanFactory.doGetBean方法中执行DefaultSingletonBeanRegistry#getSingleton方法,尝试从缓存中获取A对象的单例对象缓存。
    • 到一级缓存singletonObjects中找,未找到;
    • 到二级缓存earlySingletonObjects中找,未找到;
    • 到三级缓存singletonFactories中找,未找到;
  7. 再次执行DefaultSingletonBeanRegistry#getSingleton的重载方法,传入lamda表达式形式的ObjectFactory对象,内部调用AbstractAutowireCapableBeanFactory#createBean方法,尝试创建A对象。
  8. AbstractAutowireCapableBeanFactory#createBean方法中,调用AbstractAutowireCapableBeanFactory#doCreateBean方法,实际执行创建A对象。
  9. 实例化A对象,给字段赋值默认值后,调用DefaultSingletonBeanRegistry#addSingletonFactory方法,传入A对象的lamda表达式形式的ObjectFactory对象,将ObjectFactory对象放入三级缓存singletonFactories中,并从2级缓存earlySingletonObjects中移除(虽然这里没有),设置A对象已经开始注册。
    • 此处传入的lamda表达式,内部调用了AbstractAutowireCapableBeanFactory#getEarlyBeanReference,此方法用来执行实现了SmartInstantiationAwareBeanPostProcessor的后置处理器,比如实现AOP的AbstractAutoProxyCreator
  10. 然后开始执行A对象的AbstractAutowireCapableBeanFactory#populateBean,进行属性填充。
  11. 在进行属性填充时,发现依赖了B对象,执行AbstracBeanFactory.getBean方法,尝试获取B对象。参考上面步骤4~9。
  12. 执行到B对象的属性填充时,发现依赖了A对象,执行AbstracBeanFactory.getBean方法&#x

你可能感兴趣的:(程序人生,面试经验,程序员,java,开发语言,后端,spring)