文章目录
内容总结
refresh - invokeBeanFactoryPostProcessor
Spring 启动过程中, 在 refresh 的 invokeBeanFactoryPostProcessor 阶段, Spring 会获取到系统中所有的 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 组件, 两者的关系与功能如下
- BeanFactoryPostProcessor
- postProcessBeanFactory, 可以从 BeanFactory 中获取并修改 BeanDefinition
- BeanDefinitionRegistryPostProcessor, 是 BeanFactoryPostProcessor 的子接口, 拥有其父接口的能力
postProcessBeanFactory
, 可以从 BeanFactory 中获取并修改 BeanDefinitionpostProcessBeanDefinitionRegistry
, 可以向 BeanFactory 中注册 BeanDefinition
Spring 在 invokeBeanFactoryPostProcessor 阶段, 会将这两种组件分组并排序, 先执行所有 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法, 再执行所有 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法
在这些组件中, 有一个 ConfigurationClassPostProcessor
, 负责配置类的扫描与解析工作, 它实现了 BeanDefinitionRegistryPostProcessor 接口, 即同时实现了 postProcessBeanDefinitionRegistry 与 postProcessBeanFactory 两个方法, postProcessBeanDefinitionRegistry 方法内就会做配置类的解析, 扫描, 注册 BeanDefinition 等工作
ConfigurationClassPostProcessor - postProcessBeanDefinitionRegistry
有注册 BeanDefinition 的能力, 大致流程
- 首先获取到所有 BeanDefinition 的 Names, 大致有如下内容
- new AnnotationConfigApplicationContext 时传入的配置类, 在内部被注册
- new AnnotatedBeanDefinitionReader 时注册的一些基础架构组件
- 因为还没有开始扫描并注册自定义 BeanDefinition, 所以这里不会包含这些
- 遍历并获取对应的 BeanDefinition, 根据缓存判断是否是已经解析过的配置类
- 如果该 BeanDefinition 有 configurationClass 属性, 则说明该 BeanDefinition 是一个配置类, 且已经被解析过了, 不需要再次解析
- 如果没有解析过, 则判断该 BeanDefinition 是否符合配置类的定义, 符合配置类定义的会被添加到候选项列表中
- 如果候选列表是空的, 说明没有找到配置类, 该方法终止
- 如果有找到候选项, 排序, 实例化 BeanNameGenerator 和 Environment
- 实例化一个配置类解析器
ConfigurationClassParser
- 使用配置类解析器来解析配置类 (parse 方法), 如果解析的过程中发现了新的其他配置类, 则递归解析之 (parse 方法)
- 注意: 解析是生成 BeanDefinition, 并不会生成 Bean
什么是配置类?
ConfigurationClassUtils.checkConfigurationClassCandidate
有 @Configuration 注解, 且注解的 proxyBeanMethods 属性的值为 true, 则该类是配置类, 且是 FULL 类型的
有 @Configuration 注解, 但注解的 proxyBeanMethods 属性的值为 false, 则该类是配置类, 但是是 LITE 类型的
没有 @Configuration 注解, 且类不是接口, 且满足下面两个条件中的任意一个, 同样是配置类, 是 LITE 类型的
类上有 @Component, @ComponentScan, @Import, @ImportResource 中的任意一个
类里有 @Bean 注解标注的方法
- 在确定配置类是 FULL 或 LITE 之后, 都会在对应的 BeanDefinition 中设置对应标记
解析配置类 ConfigurationClassParser - parse
配置类解析器, 用来解析配置类
部分工具用法说明
@Conditional
, 可以传入一个实现 Condition 接口的类的数组 (多个条件), 该接口有一个 matches 方法, 返回 true 表示该条件符合, 该注解里所有条件都返回 true, 累才会被解析并注册. 该注解可以用在类上或方法上@ConditionalOnXxxx
, Springboot 里面的这一堆制动配置条件注解就是依赖 @Conditional 注解实现的
@Component
, 如果一个类上有该注解及其其衍生注解, 如 @Controller, @Service, @Repository 等, 则 Spring 扫描过程中, 该类会被扫描成为 BeanDefinition, 最终被创建为 Bean@ComponentScan
, Spring 会扫描通过该注解指定的包及其子包, 并查找带有@Component 注解及其其衍生注解, 如 @Controller, @Service, @Repository 等的类, 将其注册成为 BeanDefinition, 最终被创建为 Bean@ComponentScans
, 里面可以放多个 @ComponentScan, 每一个子项都将依次被扫描@Import
, 通过该注解将类的数组引入 Spring, 和 @Bean 有部分功能重叠- 如果被引入的类是
ImportSelector
, 该接口定义了 selectImports 方法, 返回的是一组全限定类名数组, 这些类最终被注册成为 BeanDefinition, 而实现 ImportSelector 的类则不会被注册成为 BeanDefinition- 如果被引入的类是
DeferredImportSelector
, 延迟导入, ImportSelector 在解析配置类的时候就顺带处理掉了, 而 DeferredImportSelector 在解析配置类时只是将其加入到一个容器中, 等到这一波所有的配置类都解析完成后才会处理
- 如果被引入的类是
- 如果被引入的类是
ImportBeanDefinitionRegistrar
, 该接口可以向 BeanFactory 注册 BeanDefinition, 定义了两个重载的 registerBeanDefinitions 方法, 一个需要指定 BeanName, 一个则使用名称生成器 如果被引入的类不属于上述情况
, 则该类被注册成为 BeanDefinition, BeanName 是全限定类名, 而通过 @Bean 注解的方法注册的 BeanDefinition, BeanName 是方法名
- 如果被引入的类是
@ImportResource
, 用于导入 XML 配置文件@PropertySource
, 用于让 Spring 加载特定的属性配置文件到 Environment, 通过 @Value 获取并使用之. 和 @ConfigurationProperties 组合使用, 可以将属性文件与一个类绑定, 将属性文件中的变量值注入到该类的成员变量中@PropertySources
, 里面可以放多个 @PropertySource
大致流程
配置类解析器, 根据参数不同提供了多种重载的解析方法 parse
- 判断配置类上是否有 @Conditional 注解, 有点话, 传入的所有条件都满足才会解析该配置类, 否则或跳过该配置类的解析
- 递归解析配置类及其所有层级的父类
- 判断配置类上是否存在 @Componemt 注解, 有的话则解析该配置类的内部类是否符合配置类定义, 符合的话则
解析该配置类
- 判断配置类上是否存在 @PropertySource 注解引入了某个配置文件, 有的话将其内容添加到 Environment 中
- 判断配置类上是否存在 @ComponentScan 注解, 有的话则扫描并注册 BeanDefinition, 检查扫描到的 BeanDefinition 中是否有符合配置类定义的, 复合的话则
解析该配置类
.解析配置类过程中唯一一个会注册 BeanDefinition 的过程
- 判断配置类上是否存在 @Import 注解, 有的话则获取其导入的类, 调用 processImports 方法处理, 判断导入的类的类型
- 如果导入的类是 ImportSelector, 实例化导入的这个类
- 如果同时是一个 DeferredImportSelector, 延迟, 将该实例强转并加入到配置类解析器的 deferredImportSelectors 属性中, 等到这一波所有的配置类都解析完成后才会处理
- 如果只是一个 ImportSelector, 调用 selectImports 方法拿到二次导入的全部的类, 递归调用 processImports 方法来解析二次导入的全部的类
- 如果导入的类是 ImportBeanDefinitionRegistrar (有注册 BeanDefinition 的能力), 实例化该导入的类, 然后把之添加到配置类的 importBeanDefinitionRegistrars 属性里面, 待后续处理
- 如果导入的类不是上述两种类型,
当做配置类进行解析
- 如果导入的类是 ImportSelector, 实例化导入的这个类
- 判断配置类上是否存在 @ImportResource 注解, 将一个 XML 配置文件, 添加到配置类的 importedResources 属性中, 待后续处理
- 判断配置类中是否存在 @Bean 注解的方法, 找到并添加到配置类的 beanMethods 属性中, 待后续处理
- 判断配置类实现的接口中是否有默认的 @Bean 注解的方法, 找到并添加到配置类的 beanMethods 属性中, 待后续处理
- 如果配置类有父类则获取并返回父类, 没有父类则返回 null,
把父类当做配置类去解析
- 判断配置类上是否存在 @Componemt 注解, 有的话则解析该配置类的内部类是否符合配置类定义, 符合的话则
BeanDefinition 覆盖
- 两个不同类型但指定了相同 BeanName 的 @Component 配置, 不会覆盖而是直接报错, BeanDefinition 冲突
- 两个 @Bean 方法, 但是有一个有参数有一个无参数, 不会报错, 而是会走推断构造方法类似逻辑, 找参数多的那个来调用. 当解析到第二个的时候, 直接返回中止流程
- @Bean 和 @Component, 比如各定义了一个 UserService, @Bean 生成的 BeanDefinition 叫做 ConfigurationClassBeanDefinition, @Component 被扫描到生成的 BeanDefinition 叫做 ScannedGenericBeanDefinition, 发生重复时候, 会判断类型, @Component 会先被处理, @Bean 后处理, 后面的会生成新的 BeanDefinition 并覆盖前面的, 然后重新注册到容器中. 注册的时候会判断是否存在, 是否允许覆盖, 默认允许覆盖. 所以, 如果某个类同时存在 @Bean 和 @Component 两种配置, 通常是以 @Bean 的配置为准的, 包括其 @Lazy 和 @Scope 等信息
ConfigurationClassPostProcessor - postProcessBeanFactory
有修改 BeanDefinition 的能力, 在这个阶段, 所有的 BeanDefinition 都已经被扫描并注册, 但是还没有生成 Bean
@Configuration(proxyBeanMethods=true)
配置类注解 @Configuration 有什么用? 只有加 @Configuration
或 @Configuration(proxyBeanMethods=true)
(proxyBeanMethods 默认为 true) 的配置类叫做 FULL 配置类, 其他的配置类都叫做 LITE 配置类. FULL 配置类在 BeanFactoryPostProcessor 的 postProcessBeanFactory 阶段被做强化处理, 内部的 @Bean 方法被代理, 每次调用都会从单例池中获取单例 Bean, 不然每次调用都会生成不同的对象 (创建 Bean 的时候也会调用 @Bean 方法)