总文档 :文章目录
Github : github.com/black-ant
一 . 前言
Spring IOC 体系是一个很值得深入和研究的结构 , 只有自己真正的读一遍 , 才能有更好的理解.
这篇文章主要说明一下 CreateBean 整个环节中的大流程转换 , 便于查找问题的原因 :
此篇文章的目的 :
- 梳理Bean 的创建流程 , 便于后续查找问题点
- 梳理过程中的参数情况 , 减少Debug的需求
- 梳理整体家族体系
Bean 创建的几个触发场景 :
- BeanFactory 的 #getBean(…) 方法来请求某个实例对象的时候
- 使用 ApplicationContext 容器时 , 会在启动时立即注册部分 Bean
二 . 流程梳理
先来看一个很常见的图 , 来源于 @ topjava.cn/article/139…
同样的 , 这篇的流程整理也是按照此流程 , 先看一下整个流程大纲
1 | java复制代码> 实例化过程 |
2.1 实例化创建
引言 : 谁调用的 ?
doGetBean 会有2种调用途径 :
一种是 ApplicationContext 加载的时候 , 会初始化当前容器需要的 Bean :
C- SpringApplication # run
C- SpringApplication # prepareContext
C- SpringApplication # applyInitializers : 调用 ApplicationContextInitializer 集合 , 分别执行对应的 initialize
C- ApplicationContextInitializer # initialize
C- ConditionEvaluationReport # get
C- AbstractBeanFactory # getBean
第二种是容器必要 Bean 加载完成后 ,refresh 时处理所有的 Bean
- C- SpringApplication # run
- C- SpringApplication # refreshContext
- C- AbstractApplicationContext # refresh()
- 此处refresh 中有多处会调用 getBean
- C- AbstractApplicationContext # invokeBeanFactoryPostProcessors
- C- AbstractApplicationContext # registerBeanPostProcessors
- C- AbstractApplicationContext # onRefresh();
- C- AbstractApplicationContext # finishBeanFactoryInitialization
- C- AbstractBeanFactory # getBean
PS : 另外还有一种就是因为依赖关系被递归调用的
2.1.1 doGetBean 入口
1 | java复制代码C171- AbstractBeanFactory |
doGetBean 可以分为 5 个阶段
- 阶段一 : 生成 beanName 后尝试从单例缓存中获取
- 阶段二 : 单例缓存中获取失败后 ,尝试 ParentBeanFactory 中获取
- 阶段三 : 依赖检查 , 保证初始化当前bean所依赖的bean
- 阶段四 : 三种不同的类型获得 Bean 实例 (Singleton / prototype / other)
- 阶段五 : 此时 Bean 已经准备完成了 , 此处检查所需的类型是否与实际bean实例的类型匹配 , 不符需要转换
1 | java复制代码// 核心方法一 : |
2.1.2 doGetBean 补充节点
PS:M171_02_01 获取 beanName
1 | java复制代码--> canonicalName(BeanFactoryUtils.transformedBeanName(name)) |
PS:M171_02_03 为什么四种情况会直接返回 ?
1 | java复制代码4.1- AbstractBeanFactory : parentBeanFactory.doGetBean |
PS:M171_02_04 对于只检查的那样处理有什么目的 ?
1 | java复制代码 |
PS:M171_02_05 三种不同方式的本质区别 ?
1 | java复制代码相同点 : 最终调用 getObjectForBeanInstance |
PS:M171_02_06 sharedInstance 和 bean 的区别
这里可以看到 , 首先通过 createBean 创建了一个 Object 对象 sharedInstance , 又通过 getObjectForBeanInstance 生成了一个 Bean ,他们有什么本质的区别 ?
1 | JAVA复制代码if (mbd.isSingleton()) { |
sharedInstance 中的 Bean 是已经走完 Bean 创建流程的 Bean了
2.1.3 AbstractBeanFactory # createBean
1 | java复制代码C171- AbstractBeanFactory |
2.1.4 AbstractAutowireCapableBeanFactory # createBean 主流程
1 | java复制代码C173- AbstractAutowireCapableBeanFactory extends AbstractBeanFactory |
doCreateBean 主流程
doCreate 分为几大步骤 :
1 | java复制代码 C173- AbstractAutowireCapableBeanFactory extends AbstractBeanFactory |
PS:M173_05_01 : 递归循环细说
1 | java复制代码 |
M173_05 doCreateBean 源码
1 | java复制代码protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { |
总结
其中有几个锚点 :
Step 1 : 入口
入口中主要是会统筹管理 , 如果缓存中有 , 则直接使用 , 如果没有 , 则区别 Scope 分别使用不同的方式获取一个 Bean
1 | java复制代码- C171- AbstractBeanFactory |
Step 2 : 创建主流程
创建主流程就是创建一个完整的 Bean , 走一个 Bean 创建的完整周期 , 包括 process , 属性注入 , init 初始化等等 , 这些我们在后面的文章中再详细说说
1 | java复制代码// Step 1 : |
附录
1 | java复制代码protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, |
参考与感谢
写这个之前 , 还跑过去再读了一遍 , 很感谢死磕系列开启了 IOC 的源码学习
本文转载自: 掘金