spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库
先前几篇都是概念的讲解:回顾下
- BeanDefinition 是物料
- Bean是成品
- BeanFactory是仓库,存储物料与成品
- ApplicationContext初始化搜集物料入库,触发生产线,取出物料生产Bean
本文研究springboot环境下,ApplicationContext的初始化, 多是处理注解形式的Bean.
重要组件
1.PostProcessorRegistrationDelegate: 代理执行post processors的工具类
postProcessor分为两种:
- BeanFactoryPostProcessor: 发生在BeanDefinition搜集入库阶段
- BeanPostProcessor: 发生在BeanDefintion生成Bean阶段
BeanFactoryPostProcessor细分又分为两种:
- BeanDefinitionRegistryPostProcessor:继承BeanFactoryPostProcessor接口。其postProcessBeanDefinitionRegistry接口方法,具有注册更多Bean的语义。
- BeanFactoryPostProcessor:偏向于修改的语义。
2.ConfigurationClass: 这个类要理解, 表示的是配置类,是对一个具有配置语义的BeanDefinition的封装. 怎么理解这个呢. 我认为可以理解为一个xml文件. 记得一开始使用spring的时候, 需要在xml中配置bean.
ConfigurationClass语义:
- 一个ConfigurationClass 可以看做一个xml文件. 代表注解: @Configuration
- 一个ConfigurationClass 中可以定义Bean. 类似一个xml中可以定义Bean信息. 代表注解@Bean
- 一个ConfigurationClass 可以引入其他ConfigurationClass, 类似xml文件中会引入其他xml文件. 代表注解:@Import,@ComponentScan等
那么,怎么判断他是一个ConfigurationClass呢?看下面
3.ConfigurationClassUtils: ConfigurationClass工具类
checkConfigurationClassCandidate()方法: 先取出BeanDefinition的注解信息.
- 判断是否是full ConfigurationClass . 判断依据是是否被@Configuration注解标识,如果是一定是ConfigurationClass
- 判断是否是isLiteConfigurationCandidate, 判断依据是否被@Component,@ComponentScan,@Import,@ImportResource注解标识.
或者有@Bean标识的方法
总的来说是看其是否可以输出Bean。判断其是否属于一个ConfigurationClass
4.ConfigurationClassPostProcessor:属于一种post processor. 实现了BeanDefinitionRegistryPostProcessor,间接实现了BeanFactoryPostProcessor. 所以具有 注册,与修改的双重功能.主要工作就是处理ConfigurationClass
5.ConfigurationClassParser: ConfigurationClass解析器.能够解析出项目中的ConfigurationClass. (类比能找到项目中所有的配置了Bean的xml)
6.ConfigurationClassBeanDefinitionReader: BeanDefintion读取器, 从ConfigurationClass中读取里面的Bean定义(类比能够从XML中读出Bean)
搜集入库:
建议配合源代码阅读
BeanDefintion的搜集发生在refresh()初始化方法中invokeBeanFactoryPostProcessors(beanFactory)阶段,执行BeanFactoryPostProcessor.

ApplicationContext委托PostProcessorRegistrationDelegate工具类执行post processors.
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()方法执行所有BeanFactoryPostProcessor

invokeBeanFactoryPostProcessors:大体分为两步
- 首先执行BeanDefinitionRegistryPostProcessor. 调用其postProcessBeanDefinitionRegistry注册BeanDefinition
- 然后执行BeanFactoryPostProcessor 调用postProcessBeanFactory 提供修改BeanDefinition功能.
ConfigurationClassPostProcessor作为一个BeanDefinitionRegistryPostProcessor. 首先被执行.
其processConfigBeanDefinitions方法:是收集BeanDefinition的开始的地方.
ConfigurationClassPostProcessor收集步骤:
- 从仓库中找出ConfigurationClass, 因为是初期, 所以库中只有springboot项目的启动文件这个ConfigurationClass. 此ConfigurationClass也可以看做是顶级ConfigurationClass ,就类型顶级xml文件, 去关联其他xml一样. 启动文件ConfigurationClass 作为一个入口存在.
- 创建一个ConfigurationClassParser准备解析ConfigurationClass.
- 调用
**ConfigurationClassParser.parse()**解析刚找到的ConfigurationClass. 通过此入口.把所有需要ConfigurationClass找到. 就类似.通过一个xml文件把其关联的所有xml找到一样. 类比理解. - 创建一个
**ConfigurationClassBeanDefinitionReader.loadBeanDefinitions() **从找到的所有ConfigurationClass中. 遍历读取每个ConfigurationClass中的Bean定义.
分支解析:
第三步ConfigurationClassParser.parse()分支: 目的找到所有需要的ConfigurationClass.(理解为找到xml文件)
此分支又大体分为2部分:
- parse():processConfigurationClass()-->doProcessConfigurationClass():
- processDeferredImportSelectors(): 处理需要延迟处理的ImportSelector . 这里的延迟加载最终还是会走processImports()逻辑.
因为第二步最终还是走第一步,所以只研究第一步。
doProcessConfigurationClass : 解析ConfigurationClass:(解析的过程涉及到很多递归. 通过一个ArrayDeque双端栈来存储当前的ConfigurationClass)
- processMemberClasses 递归处理内部类. 检查当前ConfigurationClas的内部类是否是一个ConfigurationClass. 如果是递归processConfigurationClass()-->doProcessConfigurationClass().
- 处理@PropertySource注解
- 处理 @ComponentScan注解: 这里也有一个递归操作. 检查扫描的定义集以获取任何进一步的配置类,并在需要时递归解析.这会用ComponentScanAnnotationParser解析器找到定义集中所有被@Component标注的类. 把其也当做一个ConfigurationClass. 然后递归解析ConfigurationClass
- processImports() 递归解析@Import注解引入的配置类. @Import导入的类会分成三种情况来处理.【具体看下面processImports()解析】
- 处理@ImportResource 注解. 把引入的资源值添加当前ConfigurationClass的importedResources属性上.
- 处理@Bean:查看当前ConfigurationClass是否有@Bean的方法. 有就添加到ConfigurationClass的beanMethods属性上.
- 如果父类superclass存在,并且不是
java包中的类,并且尚未处理处理则返回它以便外层循环继续. 这也是为啥doProcessConfigurationClass会嵌套在一个do while的原因.
processImports():@Import导入的类会分成三种情况来处理.
(1)导入的是一个实现了ImportSelector接口的类.
- 如果实现DeferredImportSelector则作为延迟处理对待.放到deferredImportSelectors 缓存中在processDeferredImportSelectors()分支中处理
- 如果实现ImportSelector,执行selectImports方法递归处理可能被@Import注解的ConfigurationClass
(2)导入的是一个实现了ImportBeanDefinitionRegistrar接口的类:
- 表明有需要手动注册Bean到容器中操作. 把实现类添加到当前ConfigurationClass的importBeanDefinitionRegistrars属性中.
(3)普通类,则把他当做一个ConfigurationClass处理走递归解析.
- 当做一个ConfigurationClass递归处理.
小结:经过一些系列递归,解析后.最终的结果:就是搜集到ConfigurationClass . ConfigurationClass 里携带这各种各样的Bean定义.
从此处我们也可以看出一些东西如果向容器中注册组件
- 注解@Controller/@Service/@Repository/@Component
- @Bean 返回的Bean .
- @Import 快速导入一个组件到容器中 :普通类或者 ImportSelector接口实现类;或者ImportBeanDefinitionRegistrar实现类
- @ImportResource 导入一个xml文件。
ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()分支: 目的把ConfigurationClass中配置的BeanDefinition解析出来.(理解为解析xml文件中的Bean定义)
有了ConfigurationClass,下面就是解析. 就好比有了xml文件,就可以解析xml中的Bean定义一样.这部分讲讲ConfigurationClass中的BeanDefinition如何被解析出来.
循环遍历所有的找到的ConfigurationClass
- configClass.isImported():当前ConfigurationClass是否是通过别人通过@Import引入的,是,当做一个BeanDefinition注入到仓库中。
- configClass.getBeanMethods(): 将@Bean注解标致的方法的返回值解析成一个BeanDefinition注入到仓库中。
- loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()):加载当前ConfigurationClass中@ImportResource引入的xml文件中的Bean定义。具体是使用XmlBeanDefinitionReader读取器读取XML中配置的Bean定义, 解析成BeanDefinition注入到仓库中。
- loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()):取出实现了ImportBeanDefinitionRegistrar接口的类,执行其registerBeanDefinitions方法。注册相应的BeanDefinition到仓库中。
小结:
将ConfigurationClass中的BeanDefinition解析出来放入到仓库中即完成了 BeanDefinition的搜集工作。
回看上面的如果向容器中注册组件
除去注解@Controller/@Service/@Repository/@Component标致的类被解析ConfigurationClass本身也是一种BeanDefinition外。
loadBeanDefinitions()解析的就是针对后三种方式的注册Bean方式:
- @Bean 标注
- @Import 引入的三种。
- @ImportResource 引入的xml文件。
总结:
总结来看:BeanDefinition的搜集入库阶段,其实就是找Bean定义的配置文件(ConfigurationClass), 解析文件中Bean定义(BeaDefinition), 然后入库到仓库中的过程。
欢迎大家关注我的公众号【源码行动】,最新个人理解及时奉送。

spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库的更多相关文章
- 【spring源码分析】IOC容器初始化——查漏补缺(一)
前言:在[spring源码分析]IOC容器初始化(十一)中提到了初始化bean的三个步骤: 激活Aware方法. 后置处理器应用(before/after). 激活自定义的init方法. 这里我们就来 ...
- 【spring源码分析】IOC容器初始化——查漏补缺(二)
前言:在[spring源码分析]IOC容器初始化(八)中多次提到了前置处理与后置处理,本篇文章针对此问题进行分析.Spring对前置处理或后置处理主要通过BeanPostProcessor进行实现. ...
- 【spring源码分析】IOC容器初始化(总结)
前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正 ...
- 【spring源码分析】IOC容器初始化(二)
前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...
- 【spring源码分析】IOC容器初始化(三)
前言:在[spring源码分析]IOC容器初始化(二)中已经得到了XML配置文件的Document实例,下面分析bean的注册过程. XmlBeanDefinitionReader#registerB ...
- 【spring源码分析】IOC容器初始化(四)
前言:在[spring源码分析]IOC容器初始化(三)中已经分析了BeanDefinition注册之前的一些准备工作,下面将进入BeanDefinition注册的核心流程. //DefaultBean ...
- 【spring源码分析】IOC容器初始化(七)
前言:在[spring源码分析]IOC容器初始化(六)中分析了从单例缓存中加载bean对象,由于篇幅原因其核心函数 FactoryBeanRegistrySupport#getObjectFromFa ...
- 【spring源码分析】IOC容器初始化(十)
前言:前文[spring源码分析]IOC容器初始化(九)中分析了AbstractAutowireCapableBeanFactory#createBeanInstance方法中通过工厂方法创建bean ...
- 【spring源码分析】IOC容器初始化——查漏补缺(五)
前言:我们知道在Spring中经常使用配置文件的形式对进行属性的赋值,那配置文件的值是怎么赋值到属性上的呢,本文将对其进行分析. 首先了解一个类:PropertySourcesPlaceholderC ...
随机推荐
- 使用Pycharm轻轻松松脱下git版本控制高大上的外衣
一.思考❓❔ 1.git操作难吗? git操作命令繁杂 需求复杂场景, 使用不易 原理深邃,对初学者来说是有难度的 2.那么难,还要学吗? 作为IT行业从业者(搬砖小工),不会Git?滚,出去~~~ ...
- K8S命令-Kubectl 命令大全
参考1:https://jimmysong.io/kubernetes-handbook/guide/kubectl-cheatsheet.html?h=kubectl Kubctl 命令是操作 ku ...
- 创建基于ASP.NET core 3.1 的RazorPagesMovie项目(一)-创建和使用默认的模板
声明:参考于asp.net core 3.1 官网(以后不再说明) 本教程是系列教程中的第一个教程,介绍生成 ASP.NET Core Razor Pages Web 应用的基础知识. 在本系列结束时 ...
- 因特尔CPU上TM和R标识的区别
TM是英文trademark的缩写,TM标志并非对商标起到保护作用,它与R不同,TM表示的是该商标已经向国家商标局提出申请,并且国家商标局也已经下发了<受理通知书>,进入了异议期,这样就可 ...
- 好用的Markdown编辑器安利-Typora
Typora,一款还用极简优秀的免费开源Markdown编辑器,非常值得每一位爱好Markdown的朋友学习和使用.我个人是深深被它吸引了,不论是写博客还是记笔记,Typora都是我十足的好帮手.Ty ...
- C#实现地图坐标系的转换(WGS-84、GCJ-02、BD-09)
WGS-84坐标系:全球定位系统使用,GPS.北斗等 GCJ-02坐标系:中国地区使用,由WGS-84偏移而来 BD-09坐标系:百度专用,由GCJ-02偏移而来 (PS:源于项目需求,本来是想读图 ...
- ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射
本章主要简单介绍下在ASP.NET Core中如何使用AutoMapper进行实体映射.在正式进入主题之前我们来看下几个概念: 1.数据库持久化对象PO(Persistent Object):顾名思义 ...
- YII2数据库操作出现类似Database Exception – yii\db\Exception SQLSTATE[HY000] [2002] No such file or director
参考文章:https://blog.csdn.net/zqtsx/article/details/41845511 我的系统时Ubuntu18使用上面的方法时发现,没有MySQL.socket,然后谷 ...
- 25.Zabbix入门必备
==Zabbix入门必备== 1.配置zabbix源 [root@zabbix ~]# cat /etc/yum.repos.d/zabbix.repo [zabbix] name=Zabbix Of ...
- JS---DOM---节点的概念,属性,和获取相关的节点
回顾概念 文档: document 元素: 页面中所有的标签, 元素---element, 标签----元素---对象 节点: 页面中所有的内容(标签,属性,文本(文字,换行,空格,回车))---- ...