spring源码解析 – spring缓存和循环依赖问题

前言

由上篇的分析可知,spring创建和实例化bean的过程的环节是比较多并且包装比较深的,那么如果每次getBean时都需要走这么多环节的话,那么不但会产生很多内存对象和计算逻辑,而且更重要的是无法解决对象在一些场景中的依赖问题,尤O [ X ~ E T其是循环依赖的问题。因此spring本t + Q 4 L U +身也考虑到了这个问题,在创建bean的过程中会有一些相F M n ? m关的缓存设计。今天我们就一起来看一下它是如何用缓存来解决的。

缓存场景

场景1、首次getBean()时

首次获取bean时,根据上图的流程当从一级或~ $ `二级缓存中获取,若拿到则直接返回。若没有则直接从三级缓存中拿,当三级缓存中有缓存对象时,则通过缓存的beanFactory .getObject()直接拿到bean, 同时移除三级缓存将拿到的bean写入二级缓存中,m f ~ t Y a d f然后返回对象。
spring源码解析 - spring缓存和循环依赖问题

场景2、首次createBean()时
spring源码解析 - spring缓存和循环依赖问题
spring源码解析 - spring缓存和循环依赖问题
在doCreateBean()过程中,若bean设置可提前暴露(默认开c ! ~ y .启),则会创建三级缓存(beanFactory对象)
spring源码解析 - spring缓存和循环依赖问题
spring源码解析 - spring缓存和循环依赖问题
为什么会在创建三级缓存时,同时移除二级缓存?因为二级缓z N + 1 + k N B存中的对象是从三级缓9 x W f * f P存中H { 4 r t [ 2 获取的,所以当三级缓存更新时会同时移除老旧的二级缓存数据,避免产生缓存数据不一致问题。

循环依赖

在A对象中,持有对B对象的引用;同理在B对S { 2 M & : m v +象中,也支持对A对象的引用。这种循环a e ( 6依赖分两种情况:1.在A对象中,B对l B K .象作为A对象的成员变量;2.在A对象中,B对象作为A对象的构造参数依赖, 这两种方式一旦有相互成环的引用场景,就需要引起重* O N j视了。

以上截图属第一种情况,spring也b H F U M .只有对这种情况有考虑。这种情况spring视为正常引用,其它情况spring不支持且有此情况会直接抛出异常。
spring源码解析 - spring缓存和循环依赖问题
spring源码解析 - spring缓存和循环依赖问题

解决方案

那么对于以上的第一种循环依赖情况,spring是如何解决的呢?通过构建三级缓存机制完美解决这个问题。具体的解P k / T o j s : r决过程请看下面的流程图
spring源码解析 - spring缓存和循环依赖问题
根据上面的解决流程图和贴出的缓存源码, 详细的h $ = u解释? v H L x g W如下j = d

  1. 当Circula_ : b o e : q T JrRefA实例化时,先从缓存种拿实例bean;
  2. 当三个级别的缓存中都不存在对象时调用getSingleton()创建实例;
  3. a Y # f J G d用匿名类创建实例createBean();
  4. 通过构造函~ ! ( | &数实例化CircularRefA对象(对象中的依赖属性和成员没有被实例化,也就是L + k :Ci! 3 krcularRefB对G L V l象这时是nulle _ P);
  5. 创建第三级缓存,缓存CircularRefA对应的objectm } B V @ x 6Fac{ R G P ^ x { n 7tory对象;
  6. 对Circulal + C w 1 m w =rRefA进行依赖注入,这时触发CircularRefB对象getBean()操作;
  7. 程序递归进入步骤1....步骤6;
  8. 当同理到步= M Q R ,骤6U W L 4 A d S # 3时,在Circ& w I kularRefB中对CircularRefA进行依赖注入, 触发getBean()操作i j 2 ( E B D - P
  9. 这时从第三级缓存中可以拿到CircularRefA的引用(虽然只是个骨架),于是CircularRefA被注入;
  10. 紧接着CircularRefB实例化完成,然后通过依赖注入赋值给CircularRefA中的Cir+ i Y ! o wcularRefB( ` G B N : m r成员变量;
  11. 最终CircularRefA和CircularRefB两个对象都L i % % U - 1 t实例化完成(他们彼此持有对方的引用x j k | - Q,这时也会被赋值上);

OK, 看到这里 大家是不是明白了?spring的三个等级的缓存机制设计是很值得玩味的。感兴趣的可以深入去看下S | | J 0 , P源码
最后要明确一个结论:只有beandefintion的Scope=Singleton类型才能进行缓存和支持以上的第一H 2 _ a f种情况的循环依赖,其它类型的只要出现循环依赖,spring直接抛出异常。至于其它情况为什么spring不支R I e k持大家看过源码,应该或多或少能想得到。

结束语

关于spring缓存和循环依赖的问题d v K T [就先分享到这里。有什么关于这块的问题可以直l p 8 3 T接在评论区留言,更多spring源码的l k m 6 1 v干货请继续关注3 l & g