简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑

  • 原理解析
    • 收集@Configuration注解修饰的配置类的BeanDefinition
    • 交给ConfigurationClassParser进行循环解析
    • ConfigurationClassParser的解析流程
      • @ComponentScan
      • @Import
      • @Bean
  • 源码走读
    • 收集@Configuration注解修饰的配置类的BeanDefinition
    • 交给ConfigurationClassParser进行循环解析
    • ConfigurationClassParser的解析流程
      • @ComponentScan
      • @Import
        • ImportSelector
        • ImportBeanDefinitionRegistrar
        • @Configuration注解修饰的配置类
      • @Bean
    • 一轮parse结束,下一轮parse开始
  • 总结

每一位看到本篇文章的小伙伴,你们好,我是黄俊懿。

上一篇文章介绍了关于配置解析与BeanDefinition加载注册的源码解析,最后留下了ConfigurationClassPostProcessor的具体逻辑没有介绍。

人人都能看懂的Spring底层原理,看完绝对不会懵逼

简单易懂的Spring扩展点详细解析,看不懂你来打我

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册

本篇文件将会接着上一篇文章,对ConfigurationClassPostProcessor里面的具体逻辑进行详细解析,还是尽量做到对新手友好,尽量以图解的形式进行解析。

ConfigurationClassPostProcessor的主要作用就是解析@Configuration注解修饰的配置类里面的配置信息(比如@Bean),生成BeanDefinition,注册到容器中。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第1张图片

ConfigurationClassPostProcessor对于被@Configuration注解修饰的配置类、会解析以下几种配置信息,都是以注解的方式进行配置:

  • @Component
  • @PropertySources和@PropertySource
  • @ComponentScans和@ComponentScan
  • @Import
  • @ImportResource
  • @Bean

本篇文章主要介绍ConfigurationClassPostProcessor对 @ComponentScan@Import@Bean这三个注解的处理流程。

原理解析

收集@Configuration注解修饰的配置类的BeanDefinition

在这里插入图片描述

首先,ConfigurationClassPostProcessor会从容器中取出所有BeanDefinition,然后过滤出被@Configuration注解修饰的类的BeanDefinition,放到一个集合中

交给ConfigurationClassParser进行循环解析

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第2张图片

该集合会交给ConfigurationClassParser进行解析,ConfigurationClassParser顾名思义,就是配置类解析器,是真实负责配置类解析

解析结果会生成BeanDefinition注册到容器中,然后在此从容器中取出所有的BeanDefinition,看是否有新添加的BeanDefinition它的类是被@Configuration修饰的,如果有,要进行一轮的解析

ConfigurationClassParser的解析流程

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第3张图片

接下来就是ConfigurationClassParser里面的解析逻辑。

@ComponentScan

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第4张图片

对于 @ComponentScan 的处理,会进行包扫描,把扫描到的被@Configuration注解和@Component注解修饰的类,解析生成BeanDefinition注册到容器中

从扫描生成的BeanDefinition中,过滤出被@Configuration注解修饰的类的BeanDefinition,递归进行解析

@Import

对于@Import注解的处理,会判断导入的是什么类型

  • 如果是ImportSelector类型,会回调selectImport方法,返回类全限定名数组,然后根据这些类全限定名生成Class对象,再次进入@Import注解的处理的方法
  • 如果是ImportBeanDefinitionRegistrar类型,会收集到一个ConfigurationClass对象中,一轮解析完成后,会回调ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,方法里面是我们实现的逻辑,可以往容器中注册我们自己的BeanDefinition
  • 如果是被 @Configuration 注解修饰的类,则会递归进行该配置类的解析

@Bean

对于 @Bean 注解的处理,会把@Bean注解修饰的方法和方法所在的类的Class对象,封装成一个BeanMethod对象,也是放入到ConfigurationClass对象中,待一轮解析完毕后,会将其解析成一个BeanDefinition注册到容器中。

源码走读

上面已经对ConfigurationClassPostProcessor的原理进行讲解,下面通过代码走读进行验证。

收集@Configuration注解修饰的配置类的BeanDefinition

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第5张图片

判断该BeanDefinition对应的类上,是否被@Configuration注解修饰,如果是的话,则放入到configCandidates集合中

代码流程图:

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第6张图片

交给ConfigurationClassParser进行循环解析

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第7张图片

创建一个ConfigurationClassParser对象,然后把configCandidates集合copy到candidates集合中,然后调用ConfigurationClassParser的parse方法,解析candidates里面的BeanDefinition。

代码流程图:

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第8张图片

ConfigurationClassParser的解析流程

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第9张图片

进入到ConfigurationClassParser的doProcessConfigurationClass方法,里面是真正处理配置类解析的逻辑。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第10张图片
可以看到里面会对@ComponentScan、@Import、@Bean等注解的处理,当然还有其他的,截图没有截全。

代码流程图:

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第11张图片

@ComponentScan

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第12张图片

获取当类上所有的@ComponentScans和@ComponentScan注解,封装成一个个的AnnotationAttributes对象,放入到一个Set集合中,一个AnnotationAttributes对象包含了一个@ComponentScans注解或@ComponentScan注解的注解属性,比如包扫描路径

然后循环遍历AnnotationAttributes对象集合,调用componentScanParser的parse方法。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第13张图片

componentScanParser是ConfigurationClassParser的成员变量,类型是ComponentScanAnnotationParser,是ConfigurationClassParser专门用于处理@ComponentScan注解的解析

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第14张图片

componentScanParser的parse方法,里面会创建一个ClassPathBeanDefinitionScanner对象,该对象专门用于包扫描。获取注解上的包扫描路径,调用ClassPathBeanDefinitionScanner的doScan方法,真正进行包扫描

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第15张图片

findCandidateComponents(basePackage)方法里面进行包扫描,扫描结果返回一个BeanDefinition集合,然后遍历该集合的每一个BeanDefinition,注册到容器中

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第16张图片

返回到doProcessConfigurationClass方法,获取到包扫描返回的BeanDefinition集合,检查如果有@Configuration注解修饰的,会递归掉ConfigurationClassParser的parse方法

代码流程图:

@Import

在这里插入图片描述
@Import注解的处理逻辑,在processImports方法里面。

ImportSelector

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第17张图片

如果导入的是ImportSelector类型,会回调selectImports方法,返回一个类全限定名数组importClassNames,然后asSourceClasses方法会反射获取Class对象并封装为SourceClass对象,返回一个SourceClass类型的集合,然后递归调用processImports方法,再次进入@Import的处理逻辑。

代码流程图:

ImportBeanDefinitionRegistrar

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第18张图片

如果是ImportBeanDefinitionRegistrar类型,则把它放到ConfigurationClass对象里面,在这一轮的ConfigurationClassParser的parse方法结束,会统一进行回调。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第19张图片
简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第20张图片
简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第21张图片

回到ConfigurationClassPostProcessor的processConfigBeanDefinitions方法,可以看到,在这一轮的ConfigurationClassParser的parse方法结束后,从this.reader.loadBeanDefinitions(configClasses) 这一行代码进去,最终会看到回调ConfigurationClass里面暂存的ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,然后进入到我们自定义的注册BeanDefinition的逻辑里面。

代码流程图:

@Configuration注解修饰的配置类

回到processImports方法,看完@Import注解处理的最后一个分支。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第22张图片

如果是@Configuration注解修饰的配置类,则递归调用ConfigurationClassParser的processConfigurationClass方法,再次进入配置类的解析逻辑。

代码流程图:

@Bean

回到ConfigurationClassParser的doProcessConfigurationClass方法。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第23张图片

retrieveBeanMethodMetadata(sourceClass) 里面,反射获取Method对象,然后判断是否被@Method注解修饰,返回一个MethodMetadata类型的集合。然后再封装为BeanMethod对象,放入到ConfigurationClass对象中

也是等这一轮的ConfigurationClassParser的parse方法结束,会统一进行注册。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第24张图片

ConfigurationClassParser的parse方法结束后,也是从this.reader.loadBeanDefinitions(configClasses)进入到刚刚对ImportBeanDefinitionRegistrar进行回调的处理的地方。可以看到上面就是对BeanMethod的处理,对@Bean注解修饰的方法进行注册。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第25张图片

生成了一个ConfigurationClassBeanDefinition类型的BeanDefinition,然后给该BeanDefinition配置了factoryBeanName属性和factoryMethodName属性,表示把@Bean注解修饰的方法作为工厂方法bean,注册到容器中。

代码流程图:

一轮parse结束,下一轮parse开始

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第26张图片

把已经解析过的配置类,加入到alreadyParsed(已解析集合) 中,然后清空candidates(待解析集合),然后检查容器中是否还有被@Configuration注解修饰然后又不在已解析集合中的BeanDefinition(也就是这一轮解析新添加到容器中的配置类),如果有,则放入待解析集合中,只要待解析集合不为空,就继续进行下一轮解析

代码流程图:

至此,整个ConfigurationClassPostProcessor的核心逻辑就基本结束了。

总结

以上就是ConfigurationClassPostProcessor的核心逻辑,下面用一张图做个总结。

简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑_第27张图片

你可能感兴趣的:(Spring,spring,java,spring,boot,后端,源码解析)