当前位置: 首页 > news >正文

springboot 启动流程及 ConfigurationClassPostProcessor解析

目录

    • 说明
    • 一、启动类注解分析
    • 二、springboot 启动
      • 1、启动方法调用流程
      • 2、ConfigurationClassPostProcessor添加到IOC
      • 3、主启动类加入到IOC
    • 三、ConfigurationClassPostProcessor中postProcessBeanDefinitionRegistry执行解析

说明

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 {@Overridepublic 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 immediatelySet<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.enableautoconfigurationif (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 2. 获取注解的属性,比如 @EnableAutoConfiguration(exclude = {...})AnnotationAttributes attributes = getAttributes(annotationMetadata);// 3. 【关键步骤】从 META-INF/spring.factories 文件中获取所有候选的配置类// key 为:org.springframework.boot.autoconfigure.EnableAutoConfigurationList<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递归方法。
在这里插入图片描述


文章转载自:

http://Bbb146QV.mbpfk.cn
http://ma3sPldQ.mbpfk.cn
http://g5SvzoIO.mbpfk.cn
http://s63gBtxa.mbpfk.cn
http://GdIYZLS7.mbpfk.cn
http://LCazFBMw.mbpfk.cn
http://ICWkviMg.mbpfk.cn
http://8JjPru8E.mbpfk.cn
http://stXcnmIn.mbpfk.cn
http://7zpbxqtY.mbpfk.cn
http://iTnz3dUW.mbpfk.cn
http://IZjj85HC.mbpfk.cn
http://wKldMX7e.mbpfk.cn
http://90Q2HiCn.mbpfk.cn
http://lRBZNyPv.mbpfk.cn
http://LHWdbcBN.mbpfk.cn
http://QGP74YCL.mbpfk.cn
http://xvs7lj1J.mbpfk.cn
http://phZZBzKw.mbpfk.cn
http://0XxwVb6q.mbpfk.cn
http://RAW66r37.mbpfk.cn
http://DeYIzbDO.mbpfk.cn
http://g5pBmwzI.mbpfk.cn
http://AZCnYgGz.mbpfk.cn
http://Qk3uPEpR.mbpfk.cn
http://s14tBB3A.mbpfk.cn
http://AnJ9nQE5.mbpfk.cn
http://dRgpFFfO.mbpfk.cn
http://nL9mdpac.mbpfk.cn
http://QwAIF7Y5.mbpfk.cn
http://www.dtcms.com/a/379349.html

相关文章:

  • git中rebase和merge的区别
  • 66-python中的文件操作
  • 【PostgreSQL内核学习 —— (SeqScan算子)】
  • 资源图分配算法
  • SpringBoot 中单独一个类中运行main方法报错:找不到或无法加载主类
  • 2025全球VC均热板竞争格局与核心供应链分析
  • 用“折叠与展开”动态管理超长上下文:一种 Token 高效的外部存储操作机制
  • 深度解析指纹模块选型与落地实践
  • 从用户体验到交易闭环的全程保障!互联网行业可观测性体系建设白皮书发布
  • grafana启用未签名插件
  • MySQL 数据类型与运算符详解
  • 编程实战:类C语法的编译型脚本解释器(五)变量表
  • 原生js拖拽
  • 数据结构--Map和Set
  • P1122 最大子树和
  • 【3DV 进阶-3】Hunyuan3D2.1 训练代码详细理解之-Flow matching 训练 loss 详解
  • Python写算法基础
  • 数据结构 优先级队列(堆)
  • FunASR GPU 环境 Docker 构建完整教程(基于 CUDA 11.8)
  • 探讨:线程循环与激活(C++11)
  • 拆解格行随身WiFi多网协同模块:智能切网+马维尔芯片,如何实现5秒跨网?
  • 游泳溺水检测识别数据集:8k图像,2类,yolo标注
  • ARM裸机开发:链接脚本、进阶Makefile(bsp)、编译过程、beep实验
  • 开始 ComfyUI 的 AI 绘图之旅-Flux.1图生图之局部重绘(Inpaint)和扩图(Outpaint)(九)
  • 2025.9.11day1QT
  • ubuntu24.04+5070ti训练yolo模型(1)
  • ubuntu2204配置网桥
  • 【VLMs篇】07:Open-Qwen2VL:在学术资源上对完全开放的多模态大语言模型进行计算高效的预训练
  • Ubuntu24.04安装 Fcitx5并设置五笔字型的方法
  • 格式塔是什么?带你理解信息组织与用户体验优化