Spring三级缓存解决循环依赖问题

一、Bean对象的创建过程

一般的Bean对象如下:首先通过构造器构造一个普通对象,然后进行依赖注入,再进行一些初始化操作,初始化后根据AOP生成代理对象,最后再放入单例池map,这个单例池map就是一级缓存。
在这里插入图片描述

二、循环依赖问题

但是如果A的普通对象在依赖注入时用到了其它Bean对象,则要去单例池中进行查找,如果没有则也要进行创建,如果这个Bean反过来又依赖了A的Bean对象,那么这就构成了循环依赖。

三、三级缓存解决循环依赖

SpringBoot中是通过三级缓存来解决循环依赖的,这三级缓存都是一个Map:

  1. 一级缓存singletonobjects:就是前面的单例池map,用于存储已经创建完成的Bean对象。
  2. 二级缓存earlySingletonObjects:用于缓存提前创建的Bean对象。
  3. 三级缓存singletonFactories:用于缓存提前创建Bean对象的lambda表达式。

解决循环依赖的步骤:

  • 比如说在创建A类的Bean对象前,会将其放到一个creatingSet中,表示这个类的Bean对象正在创建,然后再创建一个A的普通对象,用这个对象构造一个lambda表达式放到三级缓存中,这个表达式可以提前创建一个AOP代理对象。
  • 如果在依赖注入时发现依赖的B对象在创建时也依赖了A,而且A还在creatingSet中,就说明发生了循环依赖,那么就会去二级缓存中去找,如果也没有就执行三级缓存中的lambda表达式,提前生成A的代理Bean对象放到二级缓存中,并注入到B对象中,这样B竟可以顺利完成Bean对象的构造。
  • 然后A的普通对象的依赖注入也能顺利完成,再进行一些初始化操作,最后再将二级缓存中的代理Bean对象(内部引用了普通对象)放到一级缓存中,完成创建。
    Spring三级缓存解决循环依赖问题_第1张图片

四、什么情况下的循环依赖不会自动解决

  1. 没有无参构造器,或者带参构造器加了@Autowired注解,Spring会自动调用带参的构造器构造普通对象,并自动注入参数,而如果这个参数对象也反过来依赖了原对象,那么这种循环依赖就没法自动解决了,因为构造器方法无法执行普通对象都没法创建出来,三级缓存中也就不存在相关的lambda表达式了。

@Lazy注解解决上述问题

在A的带参构造器方法上加上@Lazy注解,当参数对象B循环依赖了A时,会直接创建一个B的代理对象(非真正的Bean对象),那么A的Bean对象就可以顺利完成。当调用A中B代理对象的方法时,实际会调用Spring容器中的B的Bean对象的方法,这样就解决了上述循环依赖问题

参考:https://www.bilibili.com/video/BV1dP411J7tQ

你可能感兴趣的:(Java工程师,spring,java)