欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库

程序员文章站 2023-11-13 13:14:28
先前几篇都是概念的讲解:回顾下 BeanDefinition 是物料 Bean是成品 BeanFactory是仓库,存储物料与成品 ApplicationContext初始化搜集物料入库,触发生产线,取出物料生产Bean 本文研究springboot环境下,ApplicationContext的初始 ......

先前几篇都是概念的讲解:回顾下

  • 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.

spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库

applicationcontext委托postprocessorregistrationdelegate工具类执行post processors.
postprocessorregistrationdelegate.invokebeanfactorypostprocessors()方法执行所有beanfactorypostprocessor

spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库

invokebeanfactorypostprocessors:大体分为两步

  • 首先执行beandefinitionregistrypostprocessor. 调用其postprocessbeandefinitionregistry注册beandefinition
  • 然后执行beanfactorypostprocessor 调用postprocessbeanfactory 提供修改beandefinition功能.

configurationclasspostprocessor作为一个beandefinitionregistrypostprocessor. 首先被执行.
其processconfigbeandefinitions方法:是收集beandefinition的开始的地方.

configurationclasspostprocessor收集步骤:

  1. 从仓库中找出configurationclass, 因为是初期, 所以库中只有springboot项目的启动文件这个configurationclass. 此configurationclass也可以看做是*configurationclass ,就类型*xml文件, 去关联其他xml一样. 启动文件configurationclass 作为一个入口存在.
  2. 创建一个configurationclassparser准备解析configurationclass.
  3. 调用**configurationclassparser.parse()**解析刚找到的configurationclass. 通过此入口.把所有需要configurationclass找到. 就类似.通过一个xml文件把其关联的所有xml找到一样. 类比理解.
  4. 创建一个**configurationclassbeandefinitionreader.loadbeandefinitions() **从找到的所有configurationclass中. 遍历读取每个configurationclass中的bean定义.

分支解析:

第三步configurationclassparser.parse()分支: 目的找到所有需要的configurationclass.(理解为找到xml文件)

此分支又大体分为2部分:

  • parse():processconfigurationclass()-->doprocessconfigurationclass():
  • processdeferredimportselectors(): 处理需要延迟处理的importselector . 这里的延迟加载最终还是会走processimports()逻辑.

因为第二步最终还是走第一步,所以只研究第一步。

doprocessconfigurationclass : 解析configurationclass:(解析的过程涉及到很多递归. 通过一个arraydeque双端栈来存储当前的configurationclass)

  1. processmemberclasses 递归处理内部类. 检查当前configurationclas的内部类是否是一个configurationclass. 如果是递归processconfigurationclass()-->doprocessconfigurationclass().
  2. 处理@propertysource注解
  3. 处理 @componentscan注解: 这里也有一个递归操作. 检查扫描的定义集以获取任何进一步的配置类,并在需要时递归解析.这会用componentscanannotationparser解析器找到定义集中所有被@component标注的类. 把其也当做一个configurationclass. 然后递归解析configurationclass
  4. processimports() 递归解析@import注解引入的配置类. @import导入的类会分成三种情况来处理.【具体看下面processimports()解析
  5. 处理@importresource 注解. 把引入的资源值添加当前configurationclass的importedresources属性上.
  6. 处理@bean:查看当前configurationclass是否有@bean的方法. 有就添加到configurationclass的beanmethods属性上.
  7. 如果父类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的搜集入库