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

SpringBoot 启动入口深度解析:main方法执行全流程

一、main方法的启动本质

Spring Boot应用的启动入口是标准的Java main方法,但它的特殊之处在于:

@SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {//.....}

关键点

  1. JVM加载主类时不会处理类上的注解(如@SpringBootApplication
  2. 所有注解处理都由Spring容器在初始化阶段完成
  3. main方法本质是Spring容器的启动触发器

二、main方法执行瞬间的关键动作

当JVM执行main方法时,在SpringApplication.run()调用瞬间发生以下关键操作:

1. SpringApplication实例化

// SpringApplication.run()内部实现
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class[]{primarySource}, args);
}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);
}public ConfigurableApplicationContext run(String... args) {// 记录应用启动时间戳和初始化指标收集器Startup startup = Startup.create();// 如果配置了关闭钩子(默认true),则注册JVM关闭时清理资源的钩子if (this.properties.isRegisterShutdownHook()) {SpringApplication.shutdownHook.enableShutdownHookAddition();}// 创建引导上下文(BootstrapContext),用于早期初始化组件DefaultBootstrapContext bootstrapContext = createBootstrapContext();// 应用上下文ConfigurableApplicationContext context = null;// 强制设置awt.headless模式(确保无图形界面的服务器环境正常工作)configureHeadlessProperty();// 获取并初始化SpringApplicationRunListener集合(从spring.factories加载)SpringApplicationRunListeners listeners = getRunListeners(args);// 发布ApplicationStartingEvent事件(最早的生命周期事件)listeners.starting(bootstrapContext, this.mainApplicationClass);try {// 解析命令行参数(封装为ApplicationArguments对象)ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备环境配置(加载properties/yml,合并命令行参数,发布ApplicationEnvironmentPreparedEvent事件)ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 打印Banner(控制台图标,可通过spring.banner.*配置)Banner printedBanner = printBanner(environment);// 根据应用类型创建对应的ApplicationContext(Servlet/Reactive/普通)context = createApplicationContext();// 设置应用启动指标收集器context.setApplicationStartup(this.applicationStartup);// 准备应用上下文(配置Bean工厂,注册单例,发布ApplicationContextInitializedEvent事件)prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// ★核心:刷新上下文(加载配置类,初始化所有Bean,启动嵌入式服务器)refreshContext(context);// 上下文刷新后的扩展点(默认空实现,可被子类覆盖)afterRefresh(context, applicationArguments);// 记录启动完成时间点startup.started();// 如果配置了启动日志(默认true),打印应用启动信息(版本、端口等)if (this.properties.isLogStartupInfo()) {new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup);}// 发布ApplicationStartedEvent事件(上下文已刷新但未调用Runner)listeners.started(context, startup.timeTakenToStarted());// 执行ApplicationRunner和CommandLineRunner实现类callRunners(context, applicationArguments);}catch (Throwable ex) {// 处理启动失败:发布ApplicationFailedEvent事件并抛出异常throw handleRunFailure(context, ex, listeners);}try {// 检查上下文是否在运行状态(正常流程应处于运行中)if (context.isRunning()) {// 发布ApplicationReadyEvent事件(应用完全就绪,可接收请求)listeners.ready(context, startup.ready());}}catch (Throwable ex) {// 处理就绪事件阶段的异常throw handleRunFailure(context, ex, null);}// 返回已初始化的应用上下文return context;
}

执行步骤

  1. 主类App.class作为primarySources保存
  2. 推断应用类型(Servlet/Reactive/None)
  3. 加载META-INF/spring.factories中的扩展点:
    • BootstrapRegistryInitializer
    • ApplicationContextInitializer
    • ApplicationListener
  4. 记录主应用类(含@SpringBootApplication的类)

2. 主类注解的识别时机

核心结论:主类上的@SpringBootApplication注解在 容器刷新阶段(refresh() 才被真正解析,由ConfigurationClassPostProcessor触发:

  1. @ComponentScan → 扫描当前包下的@Component@Service等组件。
  2. @EnableAutoConfiguration → 通过AutoConfigurationImportSelector加载AutoConfiguration.imports中的配置类。

在这里插入图片描述

三、@SpringBootApplication注解处理流程

主类注解的真正处理发生在容器刷新阶段,由ConfigurationClassPostProcessor完成:

1、配置类识别

  • invokeBeanFactoryPostProcessors()阶段,扫描所有@Configuration类(主类因@SpringBootApplication@SpringBootConfiguration@Configuration被识别)

2、注解元数据解析

解析@SpringBootApplication组合注解中的@ComponentScan@EnableAutoConfiguration

//org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)throws IOException {//*****略*****// 处理@ComponentScanSet<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScan.class, ComponentScans.class,MergedAnnotation::isDirectlyPresent);//*****略*****for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}// Process any @Import annotations// 处理@Import(包含@EnableAutoConfiguration)processImports(configClass, sourceClass, getImports(sourceClass), filter, true);//*****略*****
}

3、三大核心注解处理

注解组件处理方式作用
@SpringBootConfiguration作为@Configuration处理标记主类为配置类
@ComponentScan触发包扫描扫描当前包及子包的@Component@Service等组件
@EnableAutoConfiguration通过AutoConfigurationImportSelector加载META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

4、自动配置加载

// AutoConfigurationImportSelector逻辑
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 从spring.factories加载自动配置类List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).getCandidates();return configurations;
}

四、main方法启动时的完整执行序列

阶段关键操作是否处理主类注解
JVM加载主类1. 加载静态字段 2. 执行静态初始化块
main方法执行1. 创建SpringApplication实例 2. 存储主类引用
SpringApplication.run()1. 准备环境 2. 创建ApplicationContext 3. 注册主类Bean定义
refreshContext()1. invokeBeanFactoryPostProcessors() 2. 触发ConfigurationClassPostProcessor
配置类处理1. 解析@SpringBootApplication 2. 执行包扫描 3. 加载自动配置类

五、核心处理组件协作图

在这里插入图片描述

六、关键设计解析

  1. 延迟注解处理
    • Spring Boot 3.5.0使用延迟注解解析策略
    • 主类在refresh()阶段才被真正处理
    • 优点:允许环境准备完成后进行条件化配置
  2. 条件注解处理
// 自动配置类的条件检查
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
public class DispatcherServletAutoConfiguration {// 仅当类路径存在Servlet和DispatcherServlet时生效
}
  1. 配置类解析顺序
1. 主配置类(@SpringBootApplication)
2. 自动配置类(AutoConfiguration.imports)
3. @ComponentScan扫描到的配置类
4. @Import引入的配置类

七、总结

核心结论:main 方法只是 Spring Boot 启动的"点火器",真正的注解解析发生在容器刷新阶段,由 Spring 的 ConfigurationClassPostProcessor引擎完成,与 Java 原生的注解处理机制完全分离。

http://www.lryc.cn/news/578272.html

相关文章:

  • 初等变换 线性代数
  • python中学物理实验模拟程序系列目录
  • Oracle 树形统计再进阶:类型多样性与高频类型分析(第三课)
  • 长短期记忆网络(LSTM):让神经网络拥有 “持久记忆力” 的神奇魔法
  • CppCon 2018 学习:An allocator is a handle to a heap Lessons learned from std::pmr
  • 【FineDataLink快速入门】01界面介绍-运维中心
  • jvm 锁升级机制
  • AI编程实战:Cursor黑科技全解析
  • AlpineLinux安装docker
  • 提示技术系列——思维树
  • 使用Verilog设计模块输出中位数,尽可能较少资源使用
  • Python 数据分析与机器学习入门 (五):Matplotlib 数据可视化基础
  • python环境快速搭建
  • Clickhouse源码分析-TTL执行流程
  • 直播 APP 开发需要多少成本
  • (LeetCode 面试经典 150 题) 135. 分发糖果 (贪心)
  • 【Springai】 2指定模型的三种方式(Ollama)
  • 【SpringAI】3.结构化输出,初级版
  • Spring Boot + ONNX Runtime模型部署
  • springboot中多个定时任务(@Scheduled)如何互不影响
  • 大数据(4)-spark
  • Webpack优化详解
  • Unity性能优化-渲染模块(1)-CPU侧(2)-DrawCall优化(2)GPUInstancing
  • 浪潮和曙光服务器的ipmi配置教程
  • 图灵完备之路(数电学习三分钟)----开关与延迟线
  • Ubuntu更换Home目录所在硬盘的过程
  • Pyhton-EXCEL与Mysql数据对比
  • 从设计到开发一个小程序页面
  • 鸿蒙NEXT-鸿蒙三层架构搭建,嵌入HMRouter,实现便捷跳转,新手攻略。(2/3)
  • HTML之常用基础标签