【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

发布时间:2022-06-20 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

盖棺

问:SPRing 是如何解决循环依赖的?

答:支持提前暴露当前正在创建的单例,这个单例可以是未完全初始化的

问:三级缓存是必要的吗?

答:非必要,两个缓存可以支持,Spring 2.5.3 版本中,只使用两个缓存来解决循环依赖。

show you the code

欢迎移步地址:三级缓存非必要 进行 clone 和 debug。

说明

以上示例,实际依赖 Spring 版本:2.5.3,该版本适用 Java7,所以需要先调整一个属性以绕过 Spring 框架的检查。

如果想要进行码 debug,可以如下点击:Download Source and Document 然后断点调试.

【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

如果想要切换三级缓存实现,可以直接修改 pom.XMl 文件中 Spring 版本,直接提升版本到 2.5.4 ,然后重新进行 Maven ReloadDownload Source and Document 即可。

【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

定论

循环依赖,表现形式很简单,就是 A -> B(A 引用了 B)B -> A(B 引用了 A),在其他技的应用场景可能会因此出现常见的死锁问题。

【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

在讨论 Spring 是如何解决循环依赖前,需要先认识两个概念:

1. 普通对象与代理对象

普通对象:指常见的通过有参/无参构造函数 newInstance 实例化后,由 Spring 进行属性赋值并执行其他初始化操作后得到的对象,与普通的 new 操作得到的对象接近;

代理对象:是 Spring 通过代理技术,如内置的 Jdk动态代理、CGLIB 代理生成的 为原始实例增加额外行为的对象

这两者有什么关系呢?

从类层级来说,代理对象类型总是低于等于(子类化)普通对象类型(或相同的 Class 类型),另外,代理对象,实际上是对普通对象加了一层包裹,代理行为在外,实际对象在内(可以理解为,Spring中,实际存在两个相同类型的对象,其中代理对象内部引用普通对象)。

【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

Spring 代理对象给循环依赖造成的棘手问题也因此而解,也就是不论最终依赖的是普通对象还是代理对象,属性的类型都足以支持进行引用赋值(可以赋值为普通对象引用,也可以赋值为代理对象引用)。

2. Spring 的 Bean 创建过程

Spring 创建 Bean 分为三个阶段

实例化:简单理解为,Spring 擅自使用 newInstance 操作,为我们调用无参构造函数,创建出一个普通对象,此时,拥有普通对象的引用但内部属性未赋值

属性填充:拥有了对象后,需要给对象内的属性赋值,如常见的 @Autowired 和 @Value 等,而注入的依赖对象可能导致循环引用

初始化:填充完内部属性后,为对象执行其他的一系列操作,此时可能在原对象的基础上,为其套上一层壳(额外行为),即为代理对象(即正常的代理对象生成阶段),最后完成对象构建过程

也就是说:在实例化、属性填充阶段,对象是不完全的,而在初始化阶段后,无论是否生成代理对象,此阶段后的 Bean 就是最终形态。

【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

3. 收尾

理解完上述两点,继续往下走。

破坏死锁的方法有许多种,而 Spring 的解决方式主要是:支持提前暴露当前正在创建的单例,这个实例可以是未完全构建的。

【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

可能有人会疑惑,如果 B对象提前加载了A的代理对象,即 B引用了代理对象 A'后完成了 B 对象本身的构建,而 A 的普通对象此时正处于填充属性阶段,可能存在其他属性需要在填充 B引用 之后进行,他们是如何关联上的?

这就回到刚刚的第一点,就是:代理对象实际是对普通对象的一个包裹,属性填充、初始化的其他操作最后都会在原始对象上进行

所以,原始对象只有一个,而最终属性依赖到的对象,是 代理对象 A' 还是 普通对象 A,只取决于它有没有被套一层壳,即使有,也因为代理技术上的支持,使得 代理对象 A' 完全可以被被赋值到 属性 A 上。

【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

加铆钉

Spring2.5.3 的源码中,开发者只使用了两个缓存来解决循环依赖,并且翻看源码,可以看到:

【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?

注释中,明确允许提前引用一个当前正在创建的单例对象用于解决循环依赖

再然后,查看 Spring2.5.4 源码、代码注释changeLOG,你也找不到任何关于新增出三级缓存的描述,在我看来,新缓存不过是作者为了更加清晰的代码流程以及为 Spring 提速而新增的属性,如果是 bug,至少会在 changelog 中提一嘴,但实际却不值一提。

回到文章开头的问题:

问:Spring 是如何解决循环依赖的?

答:支持提前暴露当前正在创建的单例,这个单例可以是未完全初始化的Spring2.5.3 使用了二级缓存,Spring2.5.4 及之后,使用了三级缓存。实际上,解决循环依赖,甚至只需要一个缓存就可以,二级缓存也不是必须的。

你能接受这个答案吗?

The End

defaultsingletonBeanRegistry:我所被给予的,不过是一个小小的集合属性,而我所赋予的,是 Java八股界的一次狂欢。

三级缓存博文何时开始是不确定的了,但泛滥在互联网中是确定的,希望每个纠结三级缓存的面试官,或即将成为面试官的 Javaer,都能够读到此文,否则,在座各位阅完有所收获的读者,恐怕只会徒增面试被涮的风险_|。

由于 Spring 目前已被迁移至 gIThub,仓库最早的源码版本是:3.0.0。在此附上 spring2.5.3spring2.5.4 完整源码文件及 changelog 下载链接(密码: othl),有兴趣可自行获取。

脚本宝典总结

以上是脚本宝典为你收集整理的【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?全部内容,希望文章能够帮你解决【Spring浅析】七、给二三级缓存盖棺定论,面试官你答案对吗?所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。