1. BD 分类

image.png

2. BD 来源

2.1. 配置类及其注入的 bean

2.2. 指定的类路径

3. RootBeanDefinition 逻辑流程⭐️🔴

^f4b3wu

https://www.bilibili.com/video/BV1Ap4y1D798/?spm_id_from=..search-card.all.click&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

35:19

3.1. 注册 Root BD

入口 :new AnnotationConfigApplicationContext(MainConfig.class)
image.png

image.png
image.png

3.2. 创建 ConfigurationClassPostProcessor

刷新第5步:invokeBeanFactoryPostProcessors()
getBean ConfigurationClassPostProcessor

image.png

image.png
%%
0827-🏡⭐️◼️ConfigurationClassPostProcessor 继承关系 ?🔜MSTM📝 继承 BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor。与其他几个对比记忆:AutowiredAnnotationBeanPostProcessor:MI, CommonAnnotationBeanPostProcessor:MI, AbstractAutoProxyCreator: ISB◼️⭐️-point-202302120827%%
^na0djy

4. 用户 BD 解析流程 (Annotated)⭐️🔴

4.1. 流程图

Annotated:https://www.processon.com/diagraming/63dd8eb4b59543238fa3a6a5
XML:https://www.processon.com/diagraming/63ddb70fc12afe0cadb59d2c

4.2. 读取配置类 (注册配置类)

4.2.1. BeanDefinitionReader 接口

image.png

 1. XmlBeanDefinitionReader 从 XML 配置中读取 BeanDefinition;
 2. PropertiesBeanDefinitionReader 从 Properties 文件读取 BeanDefinition;
 3. AnnotatedBeanDefinitionReader 对带有@Configuration 注解的BeanDefinition 进行注册;

4.2.2. AnnotatedBeanDefinitionReader⭐️🔴

^8skkg4

4.2.2.1. 注册位置

image.png
image.png

4.3. 扫描配置类 (解析配置类)

^zs22cl
作用:处理配置的 BeanDefinition 信息,把配置类中所有 bean 的定义信息导入进来。

4.3.1. 扫描入口

image.png

image.png

4.3.2. 解析流程概览

image.png

执行 ConfigurationClassPostProcessorpostProcessor.postProcessBeanDefinitionRegistry() 方法,会把【配置类中的】所有 bean 的定义信息导入进来。
期间由 ConfigurationClassParser 解析每一个配置类:
获取所有已经注册的 BeanDefinition 的 beanName,遍历中筛选对应的 beanDefinition(被注解修饰的),添加到 List<BeanDefinitionHolder> configCandidates 交给 ConfigurationClassBeanDefinitionReader 注册到 BeanDefinitionMap 中

判断当前 BeanDefinition 是否是一个配置类,并为 BeanDefinition 设置属性为 lite 或者 full:如果 Configuration 显示配置 proxyBeanMethods 代理为 true 则为 full,如果加了@Bean、@Component、@ComponentScan、@Import、@ImportResource 注解,则设置为 lite

4.3.3. ConfigurationClassParser⭐️🔴

获取并遍历所有已经注册的 BeanDefinition,通过 checkConfigurationClassCandidate 方法筛选出被注解修饰的并放入 configCandidates 集合中

dowhile 循环遍历上面一步得到的相关的 BeanDefinitionHolder 对象集合

Spring 的工具类 ConfigurationClassParser 用于分析@Configuration 注解的配置类,产生一组 ConfigurationClass 对象。它的分析过程会接受一组种子配置类 (调用者已知的配置类,通常很可能只有一个),从这些种子配置类开始分析所有关联的配置类,分析过程主要是递归分析配置类的注解@Import,配置类内部嵌套类,找出其中所有的配置类,然后返回这组配置类。❕%%
1119-🏡⭐️◼️配置类中@Import 导入的类有可能也是配置类 ?🔜MSTM📝 比如声明式事务中,注解@EnableTransactionManagement 通过@Import(TransactionManagementConfigurationSelector.class),导入了一个 AutoProxyRegistrar 和一个配置类 ProxyTransactionManagementConfiguration ◼️⭐️-point-202302121119%%

这个工具类自身的逻辑并不注册 bean 定义,它的主要任务是发现@Configuration 注解的所有配置类并将这些配置类交给调用者(调用者会通过其他方式注册其中的 bean 定义)

4.3.3.1. parse() : 外部调用入口

^lfmh74

1.将其封装成一个 ConfigurationClass
2.调用 processConfigurationClass(ConfigurationClass configClass)

分析过的每个配置类都被保存到属性 this.configurationClasses 中

4.3.3.2. doProcessConfigurationClass()

对一个配置类执行真正的处理

  1. 一个配置类的成员类 (配置类内嵌套定义的类) 也可能适配类,先遍历这些成员配置类,调用 processConfigurationClass 处理它们;
  2. 处理配置类上的注解@PropertySources,@PropertySource
  3. 处理配置类上的注解@ComponentScans,@ComponentScan
  4. 处理配置类上的注解@Import
  5. 处理配置类上的注解@ImportResource
  6. 处理配置类中每个带有@Bean 注解的方法
  7. 处理配置类所实现接口的缺省方法
  8. 检查父类是否需要处理,如果父类需要处理返回父类,否则返回 null

4.3.4. ComponentScanAnnotationParser⭐️🔴

对于非@Configuration 注解的其他 bean 定义,比如@Component、@ComponentScans、@ComponentScan 注解的 bean 定义,使用另外一个工具 ComponentScanAnnotationParser 扫描和注册它们。ComponentScanAnnotationParser 在扫描到 bean 定义时会直接将其注册到容器,而不是采用和 ConfigurationClassParser 类似的方式交由调用者处理。

4.3.4.1. 两者的关系⭐️🔴

  1. ConfigurationClassPostProcessor 中使用 ConfigurationClassParser 解析配置类过程中,使用到了 ComponentScanAnnotationParser
  2. ComponentScanAnnotationParser 处理@ComponentScans,@ComponentScan 注解。在扫描到 bean 定义时会直接将其注册到容器,而不是采用和 ConfigurationClassParser 类似的方式交由调用者处理。
  3. ComponentScanAnnotationParser 最终所使用的扫描器是 ClassPathBeanDefinitionScanner

4.3.5. ClassPathBeanDefinitionScanner

4.3.5.1. 概括描述

  1. ComponentScanAnnotationParser 中使用了 ClassPathBeanDefinitionScanner
  2. ClassPathBeanDefinitionScanner 是一个从指定包内扫描所有组件 bean 定义的 Spring 工具。
    工作时,它接收一组包的名称,然后在这些包内扫描所有的类,查找其中符合条件的 bean 组件定义并将这些 bean 组件定义注册到容器。这些 bean 定义注册到容器时具体使用的类为 ScannedGenericBeanDefinition ,这是 Spring bean 定义模型接口 BeanDefinition 的一个具体实现类,针对扫描得到的 bean 定义。

4.3.5.2. 处理注解⭐️🔴

  • @Component
    • @Repository
    • @Service
    • @Controller
      • @RestController
  • @ManagedBean (Java EE 6)
  • @Named (JSR-330

https://blog.csdn.net/andy_zhang2007/article/details/85705001

4.3.6. ConfigurationClassBeanDefinitionReader⭐️🔴

this.reader.loadBeanDefinitions(configClasses)最后一步注册的核心方法,将完全填充好的 ConfigurationClass 实例转化为 BeanDefinition 注册入 IOC 容器

该内部工具由 Spring BeanDefinitionRegistryPostProcessor ConfigurationClassParser 使用。在容器启动过程中, ConfigurationClassParser 会在 BeanDefinitionRegistryPostProcessor 应用阶段被调用,用于发现应用中所有的配置类 ConfigurationClass,然后交给 ConfigurationClassBeanDefinitionReader 将这些配置类中的 bean 定义注册到容器。

image.png

5. postProcessBeanFactory⭐️🔴

5.1. 方法作用

%%
0726-🏡⭐️◼️refresh() 第 5 步,invokeBeanFactoryPostProcessor 中,第二个方法 postProcessBeanFactory 的作用 ?🔜MSTM📝 生成 Cglib 动态代理,以及 full 和 lite 版本的判断◼️⭐️-point-20230215-0726%%

该方法是对 BeanFactory 进行处理,用来干预 BeanFactory 的创建过程。主要干了两件事: ^mcyor3

  • 对被 @Configuration 标注的类,使用 CGLIB 代理生成代理对象。
  • 向 beanPostProcessors 集合中添加一个 ImportAwareBeanPostProcessor Bean 的后置处理器

5.2. full 版和 lite 版⭐️🔴

%%
▶1.🏡⭐️◼️【🌈费曼无敌🌈⭐️第一步⭐️】◼️⭐️-point-20230323-0720%%
❕ ^4zhdzi

https://www.processon.com/diagraming/63e37dc46330b9282f70ecc0

processConfigBeanDefinitions → ConfigurationClassUtils.checkConfigurationClassCandidate

image.png

image.png

%%
0946-🏡⭐️◼️Configuration 注解的 full 和 lite 版本的区别和划分 ?🔜MSTM📝 除非显示的标识 Configuration 或者加了 proxyBeanMethods 为 true,其他情况都属于 lite 模式◼️⭐️-point-202302090946%%

  • 配置类组件之间无依赖关系用 Lite 模式,减少判断,加速容器启动过程
  • 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用 Full 模式
    %%
    ▶7.🏡⭐️◼️lite 能加快启动速度的原因 ?🔜MSTM📝 不需要判断容器中是否已经存在需要的 Bean,直接生成新的,不需要生成代理对象◼️⭐️-point-20230226-2149%%

    [[Spring的@Configuration配置类-Full和Lite模式_demon7552003的博客-CSDN博客]]
    https://juejin.cn/post/7122064723037650980

对比记忆
exposeProxy:1、Spring-基础
%%
▶8.🏡⭐️◼️为什么 Spring5.2 对应 SpringBoot2.2 之后默认 lite 模式 ?🔜MSTM📝 Spring5.2(对应 Spring Boot 2.2.0)开始,内置的几乎所有的 @Configuration 配置类都被修改为了 @Configuration(proxyBeanMethods = false),目的何为?答:以此来降低启动时间,为 Cloud Native 继续做准备。◼️⭐️-point-20230226-2154%%

在线程中暴露代理对象 : 目标方法执行时实际上是调用的动态代理的方法,动态代理会拦截并调用 invoke(JDK) 或者 intercept(Cglib) 方法,在此期间会设置代理对象到 AopContext 中,已备获取使用。
JDK 动态代理:
image.png
%%
1050-🏡⭐️◼️动态代理执行时机及动态代理对象设置到 AopContext 的逻辑清晰程度◼️⭐️-point-20230216-1050%%

Cglib 动态代理:
image.png
%%
0816-🏡⭐️◼️对比记忆 Cglib 相关配置 ?🔜MSTM📝 1. exposeProxy:是否在当前线程中暴露代理对象,如果为 true 会放到 ThreadLocal 中。2. proxyBeanMethods:是否开启 Configuration 注解的 full 模式,即是否为目标对象生成 cglib 代理对象,如果为 true 则生成代理对象。3. proxyTargetClass:是否强制使用 Cglib 代理◼️⭐️-point-202302120816%%
^uqfm1u

6. processor 关系

BeanDefinitionRegistryPostProcessor 是 bean 定义信息注册器的后置器,它的执行时机为所有的 bean 定义信息(BeanDefinition)将要被加载到容器的时候,但此时 Bean 实例还没有被实例化;它与 BeanFactoryPostProcessor 区别在于,BeanFactoryPostProcessor 执行是在 所有的 bean 定义信息已经加载到容器的时候,因此 BeanDefinitionRegistryPostProcessor 的执行优先于 BeanFactoryPostProcessor 的执行,它们的相同点是 执行时 bean 实例都没被 实例化;

7. 扩展使用

如要对配置文件进行加解密,这个可以在 bean 定义信息已加载完成,但 bean 未实例化前进行处理,这个可使用 BeanFactoryPostProcessor 处理(如 jasypt-spring-boot-starter 就是类似这种处理的);

8. 实战经验

9. 参考与感谢

9.1. 拓薪教育

https://www.bilibili.com/video/BV1Ap4y1D798/?spm_id_from=..search-card.all.click&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204

9.2. 网络笔记

https://www.cnblogs.com/coder-zyc/p/14583011.html

https://blog.csdn.net/andy_zhang2007/article/details/78549773

https://blog.csdn.net/andy_zhang2007/article/details/78579464