有关循环依赖和三级缓存的这些问题,你都会么?(四)

pivoteic
发布于 2022-6-20 17:38
浏览
0收藏

 

六、三级缓存无法解决的循环依赖场景

 

1)构造器注入无法解决循环依赖

 

上面的例子是通过@Autowired注解直接注入依赖的对象,但是如果通过构造器注入循环依赖的对象,是无法解决的,如代码下

有关循环依赖和三级缓存的这些问题,你都会么?(四)-鸿蒙开发者社区

构造器注入就是指创建AService对象的时候,就传入BService对象,而不是用@Autowired注解注入BService对象。

 

运行结果

有关循环依赖和三级缓存的这些问题,你都会么?(四)-鸿蒙开发者社区

启动时就会报错,所以通过构造器注入对象就能避免产生循环依赖的问题,因为如果有循环依赖的话,那么就会报错。

 

至于三级缓存为什么不能解决构造器注入的问题呢?其实很好理解,因为上面说三级缓存解决循环依赖的时候主要讲到,在AService实例化之后,会创建对应的ObjectFactory放到第三级缓存,发生循环依赖的时候,可以通过ObjectFactory拿到早期的AService对象;而构造器注入,是发生在实例化的时候,此时还没有AService对象正在创建,还没完成,压根就还没执行到往第三级添加对应的ObjectFactory的步骤,那么BService在创建的时候,就无法通过三级缓存拿到早期的AService对象,拿不到怎么办,那就去创建AService对象,但是AService不是正在创建么,于是会报错。

 

2)注入多例的对象无法解决循环依赖

有关循环依赖和三级缓存的这些问题,你都会么?(四)-鸿蒙开发者社区有关循环依赖和三级缓存的这些问题,你都会么?(四)-鸿蒙开发者社区

启动引导类

有关循环依赖和三级缓存的这些问题,你都会么?(四)-鸿蒙开发者社区

要获取AService对象,因为多例的Bean在容器启动的时候是不会去创建的,所以得去获取,这样就会创建了。

 

运行结果

有关循环依赖和三级缓存的这些问题,你都会么?(四)-鸿蒙开发者社区

为什么不能解决,上面在说三级缓存的时候已经说过了,三级缓存只能对单例Bean生效,那么多例是不会起作用的,并且在创建Bean的时候有这么一个判断,那就是如果出现循环依赖并且是依赖的是多例的Bean,那么直接抛异常,源码如下

有关循环依赖和三级缓存的这些问题,你都会么?(四)-鸿蒙开发者社区

注释其实说的很明白,推测出现了循环依赖,抛异常。

 

所以上面提到的两种循环依赖的场景,之所以无法通过三级缓存来解决,是因为压根这两种场景就无法使用三级缓存,所以三级缓存肯定解决不掉。

 

七、不用三级缓存,用二级缓存能不能解决循环依赖

 

遇到这种面试题,你就跟面试官说,如果行的话,Spring的作者为什么不这么写呢?

 

哈哈,开个玩笑,接下来说说到底为什么不行。

 

这里我先说一下前面没提到的细节,那就是通过ObjectFactory获取的Bean可能是两种类型,第一种就是实例化阶段创建出来的对象,还是一种就是实例化阶段创建出来的对象的代理对象。至于是不是代理对象,取决于你的配置,如果添加了事务注解又或是自定义aop切面,那就需要代理。这里你不用担心,如果这里获取的是代理对象,那么最后完全创建好的对象也是代理对象,ObjectFactory获取的对象和最终完全创建好的还是同一个,不是同一个肯定会报错,所以上面的理论依然符合,这里只是更加的细节化。

 

有了这个知识点之后,我们就来谈一下为什么要三级缓存。

 

第一级缓存,也就是缓存完全创建好的Bean的缓存,这个缓存肯定是需要的,因为单例的Bean只能创建一次,那么肯定需要第一级缓存存储这些对象,如果有需要,直接从第一级缓存返回。那么如果只能有二级缓存的话,就只能舍弃第二级或者第三级缓存。

 

假设舍弃第三级缓存

 

舍弃第三级缓存,也就是没有ObjectFactory,那么就需要往第二缓存放入早期的Bean,那么是放没有代理的Bean还是被代理的Bean呢?

 

1)如果直接往二级缓存添加没有被代理的Bean,那么可能注入给其它对象的Bean跟最后最后完全生成的Bean是不一样的,因为最后生成的是代理对象,这肯定是不允许的;

 

2)那么如果直接往二级缓存添加一个代理Bean呢?

 

  • 假设没有循环依赖,提前暴露了代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错,
  • 假设没有循环依赖,使用了ObjectFactory,那么就不会提前暴露了代理对象,到最后生成的对象是什么就是什么,就不会报错,
  • 如果有循环依赖,不论怎样都会提前暴露代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错

 

通过上面分析,如果没有循环依赖,使用ObjectFactory,就减少了提前暴露代理对象的可能性,从而减少报错的可能。

 

假设舍弃第二级缓存

 

假设舍弃第二级缓存,也就是没有存放早期的Bean的缓存,其实肯定也不行。上面说过,ObjectFactory其实获取的对象可能是代理的对象,那么如果每次都通过ObjectFactory获取代理对象,那么每次都重新创建一个代理对象,这肯定也是不允许的。

 

从上面分析,知道为什么不能使用二级缓存了吧,第三级缓存就是为了避免过早地创建代理对象,从而避免没有循环依赖过早暴露代理对象产生的问题,而第二级缓存就是防止多次创建代理对象,导致对象不同。

本文完。

 

文章转自公众号:三友的java日记

标签
已于2022-6-20 17:38:29修改
收藏
回复
举报
回复
    相关推荐