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 {//.....}
关键点:
- JVM加载主类时不会处理类上的注解(如
@SpringBootApplication
) - 所有注解处理都由Spring容器在初始化阶段完成
- 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;
}
执行步骤:
- 主类
App.class
作为primarySources
保存 - 推断应用类型(Servlet/Reactive/None)
- 加载
META-INF/spring.factories
中的扩展点:BootstrapRegistryInitializer
ApplicationContextInitializer
ApplicationListener
- 记录主应用类(含
@SpringBootApplication
的类)
2. 主类注解的识别时机
核心结论:主类上的@SpringBootApplication
注解在 容器刷新阶段(refresh()
) 才被真正解析,由ConfigurationClassPostProcessor
触发:
@ComponentScan
→ 扫描当前包下的@Component
、@Service
等组件。@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. 加载自动配置类 | ✅ |
五、核心处理组件协作图
六、关键设计解析
- 延迟注解处理:
- Spring Boot 3.5.0使用延迟注解解析策略
- 主类在
refresh()
阶段才被真正处理 - 优点:允许环境准备完成后进行条件化配置
- 条件注解处理:
// 自动配置类的条件检查
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
public class DispatcherServletAutoConfiguration {// 仅当类路径存在Servlet和DispatcherServlet时生效
}
- 配置类解析顺序:
1. 主配置类(@SpringBootApplication)
2. 自动配置类(AutoConfiguration.imports)
3. @ComponentScan扫描到的配置类
4. @Import引入的配置类
七、总结
核心结论:main 方法只是 Spring Boot 启动的"点火器",真正的注解解析发生在容器刷新阶段,由 Spring 的 ConfigurationClassPostProcessor
引擎完成,与 Java 原生的注解处理机制完全分离。