• 分析-ConfigurationClassPostProcessor原理


    目录

    一、概述

    二、运行流程

    1、refresh()

    2、processConfigBeanDefinitions()

    3、processConfigurationClass()

    4、doProcessConfigurationClass()


    一、概述

    ConfigurationClassPostProcessor是用来容器启动时处理解析容器的配置类,实现了BeanDefinitionRegistryPriorityOrdered接口。

    当容器开启组件扫描或使用ComponentScan会默认注册到容器中,否则,将如同其他的BeanFactoryPostProcessor后置处理器一样手动被声明到容器中。

    配置类通过ConfigurationClassUtils工具类判断:

    1、标识@Configuration注解

    2、标识@Component、@ComponentScan、@Import、@ImportResource、方法标识@Bean

     

    二、运行流程

    1、refresh()

    创建容器时,在refresh方法中调用invokeBeanFactoryPostProcessors方法创建运行BeanFactory后置处理器。

    进入invokeBeanFactoryPostProcessors方法的PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法, ConfigurationClassPostProcessor由于实现了BeanDefinitionRegistryPriorityOrdered接口,在如图红框被容器扫描出来并创建对象,在invokeBeanDefinitionRegistryPostProcessors方法中运行ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry方法。

    ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry方法主要是设置registryId,防止postProcessBeanDefinitionRegistry方法被多次调用,processConfigBeanDefinitions方法开始解析配置类。

    2、processConfigBeanDefinitions()

    processConfigBeanDefinitions方法中遍历容器的beanDefinition集合,通过ConfigurationClassUtils工具类找到配置类beanDefinition,将其放入configCandidates集合中。

    创建ConfigurationClassParser对象,调用对象的parse方法对配置类进行解析。

    进入parse方法,根据bd的类型进入不同的parse方法,而那些parse方法都调用一个processConfigurationClass方法。

    3、processConfigurationClass()

    先根据配置类是否标识了@Conditional,判断是否跳过不解析。

    通过doProcessConfigurationClass方法对配置类信息进行详细解析,若是当前配置类含有父类(排除java开头的类,例如Object),则会返回该配置类的父类,进行对父类进行解析,然后最后将已解析的配置类放到configurationClasses集合中,防止多次解析。

    4、doProcessConfigurationClass()

    该方法主要是解析多个spring注解,例如@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean、解析配置类的内部类等。

    进入doProcessConfigurationClass方法,先判断该配置类是否含有@Component注解(isAnnotated方法会找当前类标识的注解,包括注解内部的元注解),

    若是有,则通过processMemberClasses方法会遍历配置类定义的内部类,判断内部类是否是配置类,若是则将内部类放入candidates集合中,并遍历candidates集合调用processConfigurationClass方法解析。

    1. private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    2. Collection memberClasses = sourceClass.getMemberClasses();
    3. if (!memberClasses.isEmpty()) {
    4. List candidates = new ArrayList<>(memberClasses.size());
    5. for (SourceClass memberClass : memberClasses) {
    6. if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
    7. !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
    8. candidates.add(memberClass);
    9. }
    10. }
    11. OrderComparator.sort(candidates);
    12. for (SourceClass candidate : candidates) {
    13. if (this.importStack.contains(configClass)) {
    14. this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    15. }
    16. else {
    17. this.importStack.push(configClass);
    18. try {
    19. processConfigurationClass(candidate.asConfigClass(configClass));
    20. }
    21. finally {
    22. this.importStack.pop();
    23. }
    24. }
    25. }
    26. }
    27. }

    接着解析配置类的@PropertySource,解析配置文件并加载到容器环境中。

    解析@ComponentScan组件,扫描得到beanDefinition集合,若是扫描中包含配置类,则会调用parse方法先解析该配置类

     接下来解析@Import组件,解析详情可参考解析@Import底层原理_Just-Today的博客-CSDN博客

     解析@ImportResource

     解析@Bean

    最后判断该配置类是否继承类名不是java开头的父类,且父类不在knownSuperclasses集合中,若是有则返回该配置类的父类,继续解析父类,若是没,则返回null结束解析循环方法。

    至此,通过解析将容器中的所有配置类、通过解析注解得到的beanDefinition都被保存到容器的beanDefinitions集合中。

  • 相关阅读:
    .netcore基础知识(一)
    python-爬虫-urllib
    JDK17和JDK8完美卸载方法及新版JDK安装教程
    ETL可视化工具 DataX -- 安装部署 ( 二)
    Spring源码之invokeBeanFactoryPostProcessors扫描、BeanDefinition生成
    肖sir__面试就业课___数据库
    git笔记
    C++学习笔记(十七)
    【git】GitHub仓库没有 Contribution activity
    携程开源分布式配置系统Apollo服务端是如何实时更新配置的?
  • 原文地址:https://blog.csdn.net/weixin_37607613/article/details/126228867