共计 3528 个字符,预计需要花费 9 分钟才能阅读完成。
循环依赖就是我依赖你、你依赖我,或者 A 依赖 B、B 依赖 C、C 依赖 A …… 组成的盘根错节的依赖关系。
其实各种不同的依赖关系最终从逻辑上都能够演变为:我依赖你、你依赖我。
循环依赖大抵能够分为两种状况:
- 属性依赖:比方 A 对象有一个属性 B,B 对象有属性 A。
- 结构器依赖:A 对象的结构器中有参数 B,B 对象的结构器有参数 A。
结构器依赖是 Spring 解决不了的(指的是 A、B 不存在其余结构器,或者 Spring 须要通过相互依赖的结构器创立 A、B 对象),这种死结你本人写代码都没方法解决,Spring 当然也没有方法。
Spring 通过其特有的三级缓存机制能解决属性依赖。顺便说一下,其实 Spring 官网或相干材料中并没有提出过三级缓存的说法,也不太分明这种说法从哪儿来(或者是我疏漏了没有查到),反正很 NB、很浅近的样子,其实如果你认真读源码,换做一种实在的说法:Spring 最终通过三个 Map 的奇妙应用解决循环依赖问题 ….. 也就没有那么 NB、那么浅近、尤其是没有那么高大上了。然而不管怎么说,Spring 解决循环依赖的框架还是值得咱们认真学习把握的。因为 Spring 各路大牛的设计思维的确 #@¥%……%*(的确和你的支出会间接相干所以你肯定有必要花点工夫精力彻底搞清楚)。
三级缓存框架
先上一张图,对三级缓存有个直观印象:
道歉,画的有点 low,不过还是能阐明问题的哈。
Spring IoC 容器中当然不止是 DefaultSingletonBeanRegistry 一个容器,但三级缓存就是针对单例 Bena 来说的,其实如果只是从 Bean 容器的角度来讲,Spring IoC 容器很大水平上讲的也就是单例 Bean 的容器,原型 Bean 原本也是每次应用、每次创立,所以也就不须要缓存。只有单例 Bean 在 Spring IoC 容器中是一次创立长期驻留,驻留的中央就是三级缓存中的“一级缓存”。
如上图,三级缓存定义在 DefaultSingletonBeanRegistry 中:
- 三级缓存:存储 bean name 和 bean 工厂的容器。
- 二级缓存:存储 bean name 和 bean 实例,二级缓存中的 bean 实例刚刚实现实例化、尚未实现属性赋值,所以是半成品。
- 一级缓存:存储最终实现创立的 Bean,利用 getBean 或者主动拆卸的 Bean 就是从以及缓存获取的。
Spring 解决循环依赖的具体过程
其实认真读源码,钻研完 Spring 的 Bean 创立过程之后,循环依赖的原理就高深莫测了(尽管很简单)。
Spring 的 Bean 创立过程相对来说还是比较复杂的,钻研分明全貌还是要花费点工夫的,咱们的策略就是一步一步抽丝剥茧以及各个击破,明天的次要指标就是三级缓存解决循环依赖的过程,所以对其余过程就要疏忽。
Spring 源码,Bean 的创立过程:
AbstractBeanFactory#getBean
创立和获取 bean 的对立入口,会调用 doGetBean 办法。
AbstractBeanFactory#doGetBean
- 首先调用 DefaultSingletonBeanRegistry#getSingleton(beanName,true)办法,留神这个 getSingleton 办法是不会创立 bean 的。
- 如果没有拿到 Bean,就筹备创立 Bean
- 首先查看是否有 Dependon,有的话,先创立 Dependon
- 如果要创立的是单例 bean,则调用 getSingleton(beanName,factory)创立 bean,留神这个才会创立 bean
- 否则,如果是原型 bean… 和明天的话题无关,临时疏忽
DefaultSingletonBeanRegistry#getSingleton(beanName,boolean)
咱们下面说过了,这里不会创立 bean:
- 一级缓存存在,则间接返回
- 一级缓存不存在并且 Bean 正在创立中,从二级缓存获取
- 二级缓存获取到则返回,否则查看三级缓存
- 三级缓存有的话,调用三级缓存的 bean 工厂加工 bean,放入二级缓存后,返回,并革除三级缓存
- 否则三级缓存没有的话返回 null
DefaultSingletonBeanRegistry#getSingleton(beanName,Factory)
这个 getSingleton 负担着创立 bean 实例的工作,如果获取不到的话,会创立:
- 一级缓存存在,则间接返回
- 否则,以后 bean 放入“正在创立中”列表(singletonsCurrentlyInCreation)
- 应用 Factory 创立 Bean 实例:调用 AbstractAutowireCapableBeanFactory#createBean 办法
- 以后 bean 从“正在创立中”列表移出
- 实现创立的 Bean 从二级、三级缓存移出,放入一级缓存
咱们须要留神的是,这个办法调用实现之后,一个残缺的 bean 就被创立进去、被放入到一级缓存了。
但你要晓得的是,这个办法不会那么容易实现的,玄机就在 createBean 办法中,如果有依赖的话还会递归调用 AbstractBeanFactory#getBean 办法的,逻辑又绕回去了 ……
AbstractAutowireCapableBeanFactory#createBean
真正创立 Bean 的中央:
- 创立 Bean 实例(原始类的实例,或者 CGLIB 代理对象实例)
- 如果是单例 Bean 并且以后 Bean正在创立中 ,则调用 addSingletonFactory:作用是 创立一个工厂办法,增加到三级缓存。
- 调用 populateBean 办法,属性填充(可能会产生循环依赖啊 …)
须要特地留神的是,三级缓存就是在这儿创立的,存储的内容是一个 bean factory、应用 lamda 表达式创立,工厂办法是 getEarlyBeanReference。
getEarlyBeanReference 办法前面从三级缓存获取对象的时候会回调,作用是通过 SmartInstantiationAwareBeanPostProcessor 的 getEarlyBeanReference 办法最终调用到 wrapIfNecessary 办法,目标是判断 bean 如果须要 AOP 的话则生成他的代理对象。
三级缓存存储 bean factory、而不是存储 bean 实例的目标就是为了解决依赖注入过程中的代理对象的解决的。
这部分内容的确有点绕,是须要动动脑子认真仔细思考一下能力搞明确的。
AbstractAutowireCapableBeanFactory#populateBean
属性填充,这个过程中解决主动封装,会有依赖注入。
调用 AutowiredAnnotationBeanPostProcessor#postProcessProperties 办法。
而后,AutowiredAnnotationBeanPostProcessor#postProcessProperties 进行属性赋值。
而后,再调用 DefaultListableBeanFactory#resolveFieldValue 办法。
最初,老套路,DefaultListableBeanFactory#doResolveDependency 办法。
办法 doResolveDependency 中首先解决候选注入对象的问题,这部分代码中蕴含依赖注入是查找对象(DL)的假相,这部分不是明天的主题,跳过。
DL 查找到的必定不是待注入的对象,而是待注入对象的 beanName。
那猜测一下,拿到待注入的 beanName 之后,肯定是要拿到 bean 对象之后再注入的,Spring 会怎么解决呢?怎么依据 beanName 拿到 bean 对象呢?
其实不难猜到!
doResolveDependency 办法会调用 DependencyDescriptor#resolveCandidate 办法,resolveCandidate 办法调用 beanFactory.getBean 获取对象,其实最终调用的是 AbstractBeanFactory 的 getBean 办法。
揉一揉昏花的老眼,是否感觉到似曾相识呢?
没错,往上翻,就是咱们开始剖析循环依赖的入口处,又转回去了。
所以没错,Spring IoC 在 Bean 实例化、依赖注入的过程,就是一个一直递归调用的过程。
小结
以上,就是 Spring IoC 的 bean 的实例化以及依赖注入、三级缓存解决循环依赖的次要逻辑了,整顿一下、用几个例子推导一下,置信就能搞清楚这部分内容了。
你如果不置信的话咱们下一篇文章就来推导一下。
上一篇 Spring FrameWork 从入门到 NB -classpath 扫描和组件托管](https://segmentfault.com/a/1190000043918177)