前言

在Spring容器创建过程中,BeanFactoryPostProcessor可以动态的修改容器的内容。比如PropertyPlaceholderConfigurer可以修改bean定义的占位符。

springfox 生产 关闭_springfox 生产 关闭


ConfigurationClassPostProcessor是BeanFactoryPostProcessor的实现子类,它负责处理所有被@Configuration标注的类。

自定义一个配置类MyConfiguration,在文章中,我们会以加载SpringBoot主入口类(也是配置类)的视角解析加载过程。

springfox 生产 关闭_spring_02

源码分析

Spring容器在创建过程中,会调用所有的BeanFactoryPostProcessor用来加强BeanFactory的功能(invokeBeanFactoryPostProcessors),会先判断是否是BeanDefinitionRegistryPostProcessor的子类,是就调用其postProcessBeanDefinitionRegistry方法,后判断是否是BeanFactoryPostProcessor的子类。ConfigurationClassPostProcessor中最重要的就是postProcessBeanDefinitionRegistry方法,用来处理注册BeanDefinition。

这里具体的就不多说了,不清楚的同学可以先看我另一篇文章Spring源码之BeanFactoryPostProcessor。接下来就直接看ConfigurationClassPostProcessor中的逻辑。

建议建一个项目,跟着Debug!!!

postProcessBeanDefinitionRegistry

@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);
        // 核心代码
		processConfigBeanDefinitions(registry);
	}

核心代码 processConfigBeanDefinitions

1.【获取所有的@Configuration类】 获取当前所有的BeanDefinition,过滤出被@Configuration标注的类,如果没有任何的配置类,直接结束。

springfox 生产 关闭_加载_03


2.【对配置类排序】 根据配置类实现的Order接口排序,值越小,优先级越高。

springfox 生产 关闭_java_04


3.【检测是否有提供自定义bean名称的生成策略】

AnnotationBeanNameGenerator: 先判断注解中用户是否指定名称,没有再生成唯一name

DefaultBeanNameGenerator: 优先级为:类名 -> 父类名称$child -> 工厂bean名称$created,如果还不唯一,用计数器追加数字生成唯一

springfox 生产 关闭_spring_05


4.【创建分析器分析每个@Configuration类】

5.【parser.parse(candidates)】

在parse方法中,需要对BeanDefinition分类处理, 因主入口类被@SpringBootConfiguration(@Configuration的子注解)标注,所以走基于注解的分支。

springfox 生产 关闭_spring_06

进入parse方法
5.1【解析配置类 processConfigurationClass】

5.1.1【@Condition过滤】

springfox 生产 关闭_springfox 生产 关闭_07


5.1.2【防止重复分析】

springfox 生产 关闭_springfox 生产 关闭_08

5.1.3【迭代处理配置类以及其超类】 处理一些上面的一些判断之后,此处是解析的核心代码,返回sourceClass是当前配置类的父类,如果存在会继续处理该父类,直到JDK之外的超类。

springfox 生产 关闭_springfox 生产 关闭_09


进入doProcessConfigurationClass方法

5.1.3.1【判断是否存在@Component】 如果是个bean,首先处理它的内部配置类。过滤出所有的内部配置类,进行排序,执行 5.1【解析配置类 processConfigurationClass】

springfox 生产 关闭_加载_10

5.1.3.2【处理 @PropertySource】

springfox 生产 关闭_java_11


processPropertySource:

springfox 生产 关闭_spring boot_12

5.1.3.3【处理 @ComponentScan】 重要环节,当前解析的是主入口类,扫描@ComponentScan的路径(主入口类的扫描路径为当前类所在包)下的所有class,再根据包含过滤器、排除过滤器、@Condition等等过滤出符合条件class,解析这些bean的特征,生成BeanDefinition。再检查所有的BeanDefinition中是否有其他配置类,存在立刻去解析 5.1【解析配置类 processConfigurationClass】。

根据@ComponentScan生成BeanDefinition的详细过程在这不细分析,感兴趣的看我另一篇文章Spring源码之基于@ComponentScan生成BeanDefinition

springfox 生产 关闭_spring_13


5.1.3.4【处理 @Import】

// 处理 @Import
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

processImports: 根据@Import的value分类型处理。

  • ImportSelector: 选择并返回应该导入的类的名称,如果返回的还是Import,递归processImports方法。这里我没太明白怎么返回Import,有懂的小伙伴在评论区告诉我。
  • DeferredImportSelector: ImportSelector的子接口,有个特性是在所有的配置类加载完毕后执行。执行位置在 parser.parse(candidates)方法的最后一行。
  • ImportBeanDefinitionRegistrar::如果是注册器,这里只是保存该注册器,会在loadBeanDefinitions时调用。
  • 以上都不是,将他作为配置类导入,在最后一个else分钟中调用 5.1【解析配置类 processConfigurationClass】

springfox 生产 关闭_spring_14

5.1.3.5【处理 @ImportResource】

springfox 生产 关闭_加载_15


5.1.3.6【处理 @Bean】 获取配置文件中所有的@Bean的方法,并保存在ConfigurationClass中。

springfox 生产 关闭_java_16


5.1.3.7【处理接口默认方法上的 @Bean】

springfox 生产 关闭_springfox 生产 关闭_17

5.1.3.8【判断是否有超类】

springfox 生产 关闭_加载_18


doProcessConfigurationClass结束

如果有超类,继续执行 5.1.3【迭代处理配置类以及其超类】

parse方法结束

5.2【处理 DeferredImportSelector】在解析@Import时,如果value是DeferredImportSelector的实现类,会把这个类放到一个集合中,在所有的配置类加载完成后,调用这个handler处理这些DeferredImportSelector。

springfox 生产 关闭_spring boot_19

parser.parse(candidates)方法结束

6.【loadBeanDefinitions】

springfox 生产 关闭_spring boot_20


springfox 生产 关闭_spring boot_21


加载@ImportedResource中的配置文件里的BeanDefinition,会涉及到BeanDefinitionReader的相关知识,感兴趣的话看我另一篇文章SpringBoot源码之BeanDefinitionReader解析与自定义。


以上是ConfigurationClassPostProcessor的主要内容,都是自己的感悟所得,仅供参考 ღ( ´・ᴗ・` )比心