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

执行 ConfigurationClassPostProcessor 的 postProcessor.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