循环依赖就是我依赖你、你依赖我,或者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)