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

简介: 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.

image.png


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


image.png


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), 然后入库到仓库中的过程。


相关文章
|
10月前
|
XML Java 数据格式
探索Spring之利剑:ApplicationContext接口
本文深入介绍了Spring框架中的核心接口ApplicationContext,解释了其作为应用容器的功能,包括事件发布、国际化支持等,并通过基于XML和注解的配置示例展示了如何使用ApplicationContext管理Bean实例。
438 6
|
11月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
263 1
|
9月前
|
监控 Java 应用服务中间件
SpringBoot是如何简化Spring开发的,以及SpringBoot的特性以及源码分析
Spring Boot 通过简化配置、自动配置和嵌入式服务器等特性,大大简化了 Spring 应用的开发过程。它通过提供一系列 `starter` 依赖和开箱即用的默认配置,使开发者能够更专注于业务逻辑而非繁琐的配置。Spring Boot 的自动配置机制和强大的 Actuator 功能进一步提升了开发效率和应用的可维护性。通过对其源码的分析,可以更深入地理解其内部工作机制,从而更好地利用其特性进行开发。
288 6
|
10月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
11月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
587 4
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
11月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
127 1
|
11月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
102 0
|
Java Spring 容器
获取Spring的ApplicationContext的几种方式
简单来说就是Spring中的高级容器,可以获取容器中的各种bean组件,注册监听事件,加载资源文件等功能。
230 0
获取Spring的ApplicationContext的几种方式
|
Java 容器 Spring
获取Spring的ApplicationContext的几种方式
Application Context定义 简单来说就是Spring中的高级容器,可以获取容器中的各种bean组件,注册监听事件,加载资源文件等功能。 具体定义可以参考官网:https://spring.
1071 0