springboot 启动流程及 ConfigurationClassPostProcessor解析

发布于:2025-09-12 ⋅ 阅读:(23) ⋅ 点赞:(0)

说明

DefaultListableBeanFactory中registerBeanDefinition来注册bean的描述,创建bean时会获取遍历所有bean的描述,根据bean的描述创建对象。下面重点看调用registerBeanDefinition注册bean描述的时机和获取描述创建对象的时机。

this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);

一、启动类注解分析

@SpringBootApplication是复合注解,分为三个主要的子注解
第一个@SpringBootConfiguration也是一个复合注解子注解是@Configuration

第二个@EnableAutoConfiguration是复合索引,有两个子注解
@AutoConfigurationPackage的子注解@Import(AutoConfigurationPackages.Registrar.class)
另一个是
@Import(AutoConfigurationImportSelector.class)
第三个@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
整体来看@SpringBootApplication复合注解引入了AutoConfigurationPackages.Registrar、AutoConfigurationImportSelector两个组件(具体两个组件是什么作用可以带着疑问去看源码),同时还添加了@Configuration和@ComponentScan注解。

二、springboot 启动

1、启动方法调用流程

SpringApplication.run(RuoYiApplication.class, args)->
new SpringApplication(primarySources).run(args);
在这里插入图片描述
咱们重点看的就是上图圈出来的两个方法,一个是创建ApplicationContext,ApplicationContext 是Spring Framework的核心接口,作为IoC(控制反转)容器的核心实现,负责管理应用中所有Bean的创建、依赖注入及生命周期,并提供国际化支持、资源加载、事件发布等企业级功能,我理解就是创建了IOC容器上下文。第二个是刷新ApplicationContext ,进行后置处理器调用和Bean的创建及注入逻辑。下面代码是创建ApplicationContext 的代码,咱们是web服务所以创建了AnnotationConfigServletWebServerApplicationContext。

static class Factory implements ApplicationContextFactory {

		@Override
		public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
			return (webApplicationType != WebApplicationType.SERVLET) ? null
					: new AnnotationConfigServletWebServerApplicationContext();
		}

	}

在这里插入图片描述

下面看一下AnnotationConfigServletWebServerApplicationContext的构造器

public AnnotationConfigServletWebServerApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

创建了AnnotatedBeanDefinitionReader,继续看AnnotatedBeanDefinitionReader的构造器

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

2、ConfigurationClassPostProcessor添加到IOC

重点分析一下这个 方法AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
在这里插入图片描述
会判断容器里面有没有ConfigurationClassPostProcessor
、AutowiredAnnotationBeanPostProcessor
、CommonAnnotationBeanPostProcessor的BeanDefinition,如果没有会添加上相应的BeanDefinition到IOC,看一下registerPostProcessor方法

在这里插入图片描述

	private static BeanDefinitionHolder registerPostProcessor(
			BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {

		definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(beanName, definition);
		return new BeanDefinitionHolder(definition, beanName);
	}

registry.registerBeanDefinition进入AnnotationConfigServletWebServerApplicationContext的父类GenericApplicationContext.registerBeanDefinition,beanFactory是在GenericApplicationContext的无参构造器里面创建DefaultListableBeanFactory类型。

	public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
	}

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
	}

继续跟踪到DefaultListableBeanFactory中registerBeanDefinition,把bean的Definition放入到Map集合中,把bean的名字放入到beanDefinitionNames数组。这就到了上面说明里面的内容。

	this.beanDefinitionMap.put(beanName, beanDefinition);
	this.beanDefinitionNames.add(beanName);

3、主启动类加入到IOC

查看一下启动方法中的prepareContext,new SpringApplication的时候在构造器里面把把主启动类赋值给primarySources。
在这里插入图片描述
getAllSources方法获取了主启动类,执行load方法。
在这里插入图片描述
生成BeanDefinitionLoader类,继续调用load方法。
在这里插入图片描述
继续调用load方法,当前主启动类时Class的子类,会进入第一个if判断。
在这里插入图片描述

继续跟踪到this.annotatedReader.register(source);->
registerBean(componentClass);->
doRegisterBean->
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);->
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

可以看到上面调用栈,最终调用registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()),上面分析过会方法到Map集合。

三、ConfigurationClassPostProcessor中postProcessBeanDefinitionRegistry执行解析

看一下refreshContext
在这里插入图片描述
最后进入AnnotationConfigServletWebServerApplicationContext的父类AbstractApplicationContext中的refresh方法,重点看一下invokeBeanFactoryPostProcessors方法。
在这里插入图片描述
重点分析一下这个方法PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
1、获取类型是BeanDefinitionRegistryPostProcessor的bean的名称。
2、根据通过ioc容器创建BeanDefinitionRegistryPostProcessor类型的组件。
3、执行组件中的方法。 在这里插入图片描述

通过查看ConfigurationClassPostProcessor的继承关系,ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,所以上面的就是ConfigurationClassPostProcessor的创建过程。
在这里插入图片描述
下面看一下invokeBeanDefinitionRegistryPostProcessors方法,遍历所有BeanDefinitionRegistryPostProcessor类型组件,执行postProcessBeanDefinitionRegistry方法,这个集合中就包含ConfigurationClassPostProcessor。

private static void invokeBeanDefinitionRegistryPostProcessors(
		Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {

	for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
		StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
				.tag("postProcessor", postProcessor::toString);
		postProcessor.postProcessBeanDefinitionRegistry(registry);
		postProcessBeanDefRegistry.end();
	}
}

继续调用
在这里插入图片描述
红框中就是获取了前面放入的BeanDefinitionNames数组,数组中的数据如果添加进去的在上面分析过。
在这里插入图片描述
1、根据beanName获取BeanDefinition。2、判断是不是有@Configuration的组件。3、排序集合中BeanDefinition中order大小,order越小的越优先解析。
在这里插入图片描述
调用parser.pase方法,开始解析有@Configuration的组件。
在这里插入图片描述
遍历集合中的组件开始解析parse->processConfigurationClass->doProcessConfigurationClass
在这里插入图片描述
第一个方法是获取内部方法,如果内部方法有@Configure主键,继续递归解析。第二个方法是解析@PropertySource注解。
在这里插入图片描述
解析@ComponentScans和@ComponentScan注解,把扫描到的包路径下所有类设置成BeanDefinitonHolder集合如果是有@Configuration的类继续递归,普通的bean会registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
在这里插入图片描述
具体扫描包的逻辑在

// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

如果在@ComponentScan中没有配置BasePackages,会使用当前解析的类的包路径为basePackages,这就解释了默认扫描springboot的主启动类包路径下类。
在这里插入图片描述

调用到ClassPathBeanDefinitionScanner类中scanner.doScan(StringUtils.toStringArray(basePackages))
在这里插入图片描述
又回到说明中的逻辑,往集合中放入类描述。
在这里插入图片描述
下面看一下上面留下来的疑问,主启动类中import两个组件,
@Import(AutoConfigurationPackages.Registrar.class)
@Import(AutoConfigurationImportSelector.class)会在解析@Import注解的方法中获取。
在这里插入图片描述
获取@import注解中类名,放入到Set集合中。
在这里插入图片描述
遍历set集合,判断是不是ImportSelector类型,如果是会使用反射创建组件,执行主键的Aware方法。如果是DeferredImportSelector类型会放入到集合中后面处理。在这里插入图片描述
如果是DeferredImportSelector类型会放入到集合中后面处理,否则进入else调用组件的selectImports方法。

在这里插入图片描述
回过头看AutoConfigurationImportSelector是DeferredImportSelector,会暂时在DeferredImportSelectorHandler类中的deferredImportSelectors的list数组中。
在这里插入图片描述
继续往下看如果是ImportBeanDefinitionRegistrar,会使用反射创建组件,调用Aware初始化方法,最后存入到了ConfigurationClass中的importBeanDefinitionRegistrars的map集合中,如果既不是ImportSelector也不是ImportBeanDefinitionRegistrar会继续调用processConfigurationClass递归。
在这里插入图片描述
回过头看AutoConfigurationPackages.Registrar.class是ImportBeanDefinitionRegistrar类型的。在这里插入图片描述

在回到上一面,这个this.reader.loadBeanDefinitions负责调用刚才import的类,和@Bean注解的方法,进行registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
在这里插入图片描述
这个是处理@Bean注解的组件,
在这里插入图片描述
如果@Bean里面没有设置beanName会以类名第一个字母小写为beanName.以前很纳闷从哪实现的,现在找到地方了。
在这里插入图片描述
AutoConfigurationPackages.Registrar的register方法在这里面执行。
在这里插入图片描述
在这里插入图片描述
上面提到过这个集合中存入的是AutoConfigurationImportSelector,所以会进入if条件中
在这里插入图片描述
执行register获取了AutoConfigurationImportSelector中的AutoConfigurationGroup,往this.groupings这个map中放入key是group对象,value是DeferredImportSelectorGrouping组件。

在这里插入图片描述
DeferredImportSelectorGrouping类中放入deferedImport对象
在这里插入图片描述
继续跟踪
在这里插入图片描述
遍历map集合groupings,grouping.getImports获取DeferredImportSelectorGrouping类中放入deferedImport对象

在这里插入图片描述
执行AutoConfigurationImportSelector内部类AutoConfigurationGroup中的process方法,
在这里插入图片描述
在这里插入图片描述

// AutoConfigurationImportSelector
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    // 1. 判断自动配置是否开启(默认是开启的)
    // 对应配置项:spring.boot.enableautoconfiguration
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }

    // 2. 获取注解的属性,比如 @EnableAutoConfiguration(exclude = {...})
    AnnotationAttributes attributes = getAttributes(annotationMetadata);

    // 3. 【关键步骤】从 META-INF/spring.factories 文件中获取所有候选的配置类
    // key 为:org.springframework.boot.autoconfigure.EnableAutoConfiguration
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

    // 4. 去除重复的配置类(防止多个 Jar 包都有 spring.factories 定义)
    configurations = removeDuplicates(configurations);

    // 5. 根据 @EnableAutoConfiguration 的 exclude 和 excludeName 属性,排除不需要的配置类
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);

    // 6. 【核心过滤】根据条件注解(@ConditionalOnClass, @ConditionalOnMissingBean 等)进行过滤
    // 这是自动配置“智能”与否的关键!
    configurations = getConfigurationClassFilter().filter(configurations);

    // 7. 触发自动配置导入事件,让监听器可以做一些事情
    fireAutoConfigurationImportEvents(configurations, exclusions);

    // 8. 返回最终决定要导入的配置类列表
    return new AutoConfigurationEntry(configurations, exclusions);

DeferredImportSelector: 这是 ImportSelector 的一个子接口。这是最关键的一点。它表示导入选择的执行会被延迟,直到所有 @Configuration 类都被处理完之后。这确保了自动配置可以在用户自定义的 Bean 被注册之后再进行,从而能够根据完整的上下文(如有无用户自定义的 DataSource Bean)做出正确的条件判断。
最终再次进入解析Configuration递归方法。
在这里插入图片描述