Spring Bean加载过程
- resourceLoader.getResources(location):文件加载
- loadBeanDefinitions(resources):初始化Bean定义
- doloadDocument(imputSource,resource):加载xml文件里的document
- registerBeanDefinitions(doc,resource):注册Bean定义
- parseBeanDefinitions():解析bean定义
- 加载非懒加载的单例bean
- 如果有引用其它bean,向初始化该bean的Factory放入缓存
- 直至依赖的bean初始化完毕后再初始化该bean
- 从缓存根据beanname获取bean对象
- 调用
全流程时序图看附件
我们这里主要看循环引用的时序图
解释:
- 如果存在循环依赖A->B;B->C;C->A
- 首先A进行初始化,实例化过程中并不是真正的实例化而是将自己提前暴露(放入缓存singleFactories,key:beanName;value:初始化这个bean的Factory)
- 初始化过程中发现引用B,这个时候B跟A一样进行提前暴露;B发现引用C,提前暴露
- C初始化过程中发现引用A,这个时候先去缓存singleFactories里取到A对应的BeanFactory,然后调用getObject()方法取得实例,然后C初始化完成(真正的初始化完成,而非提前暴露),将自己放入缓存singleObjects
- B去缓存singleObjects获取到C,至此B初始化完毕放入缓存,A拿到B,A真正初始化完毕放入缓存
- 至此全部初始化完毕,调用的时候只需要从缓存获取bean即可
题外话:解决循环依赖的重点在于初始化的时候并不是真正的初始化而是初始化该bean对相应的Factory(到达提前暴露的目的),有其它Bean依赖该bean是先从singletonObjects获取,如果没有获取对应的Factory,调用getObject()方法获取对象引用;这样从后到前依次创建成功
这里是递归调用,打断点的时候带着思想,容易乱
来个图《Spring源码深度解析》
公众号主要记录各种源码、面试题、微服务技术栈,帮忙关注一波,非常感谢