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

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

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

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

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

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

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

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

在这里插入图片描述

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

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

在这里插入图片描述

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

原理解析

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

在这里插入图片描述

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

交给ConfigurationClassParser进行循环解析

在这里插入图片描述

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

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

ConfigurationClassParser的解析流程

在这里插入图片描述

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

@ComponentScan

在这里插入图片描述

对于 @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

在这里插入图片描述

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

代码流程图:

在这里插入图片描述

交给ConfigurationClassParser进行循环解析

在这里插入图片描述

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

代码流程图:

在这里插入图片描述

ConfigurationClassParser的解析流程

在这里插入图片描述

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

在这里插入图片描述
可以看到里面会对@ComponentScan、@Import、@Bean等注解的处理,当然还有其他的,截图没有截全。

代码流程图:

在这里插入图片描述

@ComponentScan

在这里插入图片描述

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

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

代码流程图:

在这里插入图片描述

@Import

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

ImportSelector

在这里插入图片描述

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

代码流程图:

在这里插入图片描述

ImportBeanDefinitionRegistrar

在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

代码流程图:

在这里插入图片描述

@Configuration注解修饰的配置类

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

在这里插入图片描述

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

代码流程图:

在这里插入图片描述

@Bean

回到ConfigurationClassParser的doProcessConfigurationClass方法。

在这里插入图片描述

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

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

在这里插入图片描述

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

在这里插入图片描述

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

代码流程图:

在这里插入图片描述

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

在这里插入图片描述

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

代码流程图:
在这里插入图片描述

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

总结

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

在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值