方案导入
循环依赖是什么
构造出两个对象A和B,A中有成员B,B中有成员A,换成代码就是这样子。
循环依赖会带来什么问题
如果创建A对象,那必须注入B对象,注入B对象又需要创建A对象,如此反复,直到OOM
如何解决循环依赖问题
我们想象有这样一幅有向图,我们从A开始,到D结束,当执行到D的时候,D还是会继续执行,因为D不知道A是否被经过。
为了解决这一问题,我们只需要将经过的路径点染色
就好了。
D发现A已经是红色,知道已经被途径过,主动结束循环。
我们很容易就能发现,循环依赖问题和图路径中是否有环问题是一样的,就能保证Bean(实例)不被重复创建。
联系Spring
都说Spring三级缓存解决了循环依赖问题,那我们就使用了一级缓存就解决了缓存依赖问题,spring的开发团队怎么会傻到用三级缓存解决问题,当然这句话可能还有一个歧义,第三层缓存区解决了缓存依赖问题,这同样也是错的,且听下文分析。
三级缓存是什么
三级缓存如何工作 - 源码部分
我会对关键代码打注释,你这么聪明肯定一下就看懂了
当我们获取想要注入的单例时,有以下代码,删去了 try - catch的异常处理和解决并发问题的代码块,便于清晰的阅读
可见 getSingleton
的路径是 一级缓存 → 二级缓存 → 三级缓存,同时当从三级缓存中获取到早期对象时,直接放入二级缓存,删除三级缓存(后续的多次引用也是二级缓存),可见二级缓存+短暂的三级缓存相当于标记bean为已实例化,所以依赖三级缓存解决循环依赖显然是错的
那三级缓存(工厂)到底存储着什么,不是二级缓存就能解决问题了吗?我们在 doCreateBean
中可以看到以下代码。
证明了三级缓存以及二级缓存中的对象是引用对象,未被真正初始化,等于是一个懒加载,同样也不会造成循环依赖,因为其内部的对象没有只引用了实例化的对象,未被初始化。
getEarlyBeanReference
应该有关于Factory中的一些信息
倘若没有经过字节码增强代码可以缩略成两行
诶卧槽,这不是啥也没干!
三级缓存只参与了AOP对象的返回,解决bean的AOP代理问题
下图展示了bean的初始化过程
那我们现在可以得出以下结论了:
- 第一级缓存用来简单返回缓存后的bean对象。
- 第二级缓存就可以解决循环依赖问题。
- 二三级缓存只保留了实例化的bean,未初始化,不会导致循环依赖。
- 第三级缓存用来解决Bean的代理类的实例化问题。
如果这篇文章对你有所帮助,请给我点个赞。你的肯定会让我非常开心。🥰🥰🥰