搞不懂『三级缓存』?与生命周期冲突?为什么代理对象二级缓存不可用?进来看看,助你掌握概念思路

先听听概念

一级缓存:singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
二级缓存:earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
三级缓存:singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

Spring的设计

把概念转换下,我们先来看Spring的设计,仅仅是先琢磨下它的设计:
一级缓存:完全初始化的bean对象
二级缓存:半成品bean对象/半成品代理对象(未注入属性)
三级缓存:工厂对象,缓存刚实例化的bean(未注入属性)第一次获取实例化,第二次取创建代理对象

转换后不是特别能理解,而且出现更多的疑惑了,并且好像又跟bean的生命周期关联上了,其实研究三级缓存始终绕不开生命周期这个概念,生命周期包含:实例化、注入属性、初始化。而三级缓存就是解决在bean创建过程中的问题。

不理解的问题

看到这,是否会有这样的疑惑:

  1. Spring二级缓存存的是没有注入属性的半成品bean,三级缓存好像存的还是一个未注入属性的实例化bean?是不是重复了,概念好模糊啊!
  2. 为什么说能解决缓存依赖但不能解决aop的循环依赖?
  3. ab举例,创建b时,a不也是在没有注入属性的情况下生成的代理对象?跟生命周期的定义冲突啊!
  4. Spring的bean周期指的是实例化、注入属性、若有需要生成代理、完成初始化,那到底代理是在哪一步?

在这里插入图片描述

太容易乱了,这些是不是你理解上的痛点?

问题解答

第一个问题点先跳过,最后会通过一句话让你理解。

第二个问题:为什么说能解决缓存依赖但不能解决aop的循环依赖?

如果只有二级缓存,依旧拿ab举例
不需要代理:

  • 初始化a放入二级缓存,创建b、注入二级缓存里的a、完成初始化、放入一级缓存,回到a的创建、一级缓存中取到b注入属性、完成实例化。

循环依赖解决了对不对,完事!睡觉吧!哈哈哈,开个玩笑,这里能得到结论:如果没有代理对象的侵入,循环依赖是可以解决的。

我们来理需要代理的时候:

  • 初始化a后,由于第一步只做了实例化,并没有生成代理,那么此时的a放在哪里?a是不是没地方去放(记好这里)
  • 注入属性发现需要b对象,实例化b、注入属性,当从缓存中找a代理时没有发现(因为二级缓存没有存原始a的半成品,存储的是需要代理的a的半成品),并且没有缓存a的实例化(没有三级缓存),跟创建a时遇到的情况一模一样,那么b搁置又去创建a… 好像循环了。
  • 如果a实例化后直接放入二级缓存注入的就是原始bean,与代理bean背道而驰。

第四个问题: Spring的bean周期指的是实例化、注入属性、若有需要生成代理、完成初始化,那到底代理是在哪一步?

继续理:第二个问题解决了,第三个问题想必还在膈应着你,但还是要先说第四个问题:这是一个概念,spring设计上就是在注入属性后,初始化前生成代理。但这是Spring的设计,并不是不可以

那么看到此时,我们又多了疑问:

  • 疑问一:如果说可以违背Spring在三级缓存上的设计,直接对实例化的a创建代理对象,为什么不能在实例化a后生成代理,然后再放入二级缓存
  • 疑问二:又或者放入初始化的bean,拿出的时候判断是否需要代理,然后再生成被注入到b中?(能想到这就理解上面第四个问题的解释了,是不是感觉Spring的设计也存在不合理啊。要知道你强哥还是你强哥,继续往下看)
    在这里插入图片描述

第三个问题:ab举例,创建b时,a不也是在没有注入属性的情况下生成的代理对象?跟生命周期的定义冲突啊!

从第四个问题应该能回到自己的思路了吧?bean周期只是Spring的设计,上学老师还说不让抽烟呢,一个道理,那么此刻如果让你设计如何使用二级缓存解决代理的问题,是不是思如泉涌,但只有两个方法

  1. 每次创建完实例对象就生成代理放入二级,需要时直接引用。对应上面的疑问一。
  2. 创建完实例对象找个地方缓存,需要代理时生成并放入二级。对应上面的疑问二。

这样是不是算是可以解决了,再仔细看下第二个方式,像不像三级缓存?Spring选择的是后者,因为后者可以满足Spring自己设计的bean生命周期,总不能搬起石头砸自己的脚吧?那第一种有没有弊端,如果每个bean都直接生成代理放入,那不需要代理的bean???

悟了吗,此时强哥看到你质疑它:我想吃鱼了
在这里插入图片描述

回到第三个问题,还是要解释下:

  • 为什么实例化后没有注入属性还能生成代理?这跟bean设计对不上,还是一句话,bean的生命周期只是Spring的一个设计,但不是行不通。试想new了一个对象不注入属性,是否可以对它通过aop进行代理,可以吧通过切点在无参构造上,是不是可以生成代理对象?答案是可以生成代理对象的,但是会不符合bean生命周期的一个设定。

  • 为什么要设计出不符合生命周期的概念?单看循环依赖,如果鸡生蛋蛋生鸡,那么能解开?自然不能,所以Spring不得已才会对三级缓存中取出实例化未注入属性的bean创建代理对象,放入二级缓存。刚开始a的半成品代理是无法避免的解决方案。

这叫什么?当所有人都穷的时候,怎么说?先富起来一批人,带动下一批人富(富人:管你死活

在这里插入图片描述

第一个问题:Spring二级缓存存的是没有注入属性的半成品bean,三级缓存好像存的还是一个未注入属性的实例化bean?是不是重复了,概念好模糊啊!

其实二三级本身区别是半成品的对象、实例化的对象,实例化的原始对象不可以被引用的,因为还没有判断是否需要生成代理对象。

补充
了解了Spring的三级缓存设计,我们会得出一个疑惑:真的只有两层缓存可以解决循环依赖吗?
下面基于Spring的缓存机制,我们来反推一下,首先是一级缓存是肯定要的,那么就是二三级的选择:

  • 选择三级缓存,证明只有原始的实例化a和成品a,将a从三级中取出的代理对象,被b引用,b创建好后,a无法缓存。再次从三级中又获取一个a的代理,完成创建就打破了单例对象。
  • 选择二级缓存,说明a是半成品,但Spring第一次创建a时只实例化,没有直接生成代理,要么原始a放入二级,那么a原始对象就无法被代理,直接被其他bean引用,要么丢弃a(因为第一次请求工厂对象只会实例化a),b没有获取到又会重复以上操作

到此结束,因为大多是概念上对理解的误导,这里并不是结合源码,而是通过反向思维去验证三级缓存机制设计的意义,如果有不对的地方欢迎讨论。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值