SpringBoot启动项目详解

SpringBoot 的启动过程是一个整合 Spring 核心容器、自动配置、嵌入式服务器等功能的复杂流程,核心目标是 "简化配置、快速启动"。下面从入口类开始,逐步拆解其详细启动步骤:

一、启动入口:@SpringBootApplicationmain方法

SpringBoot 应用的启动入口是一个带有@SpringBootApplication注解的类,其中的main方法是程序的起点:

复制代码
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        // 核心启动方法
        SpringApplication.run(MyApplication.class, args);
    }
}

这行代码看似简单,却包含了初始化 Spring 容器、触发自动配置、启动嵌入式服务器等一系列操作。

二、@SpringBootApplication注解的核心作用

@SpringBootApplication是一个 "复合注解",它整合了三个关键注解,为启动过程奠定基础:

  1. @SpringBootConfiguration :本质是@Configuration,标识当前类是一个配置类,允许通过@Bean定义 Bean。
  2. @ComponentScan :自动扫描当前类所在包及其子包下的@Component(包括@Service@Controller等)注解类,将其注册为 Spring Bean。
  3. @EnableAutoConfiguration:SpringBoot 的 "灵魂",开启自动配置功能,通过加载预设的配置类,自动配置 DataSource、Web 服务器等组件。

三、SpringApplication.run()的详细流程

SpringApplication.run(MyApplication.class, args)是启动的核心方法,可拆解为两大阶段SpringApplication实例初始化 + run()方法执行。

阶段 1:SpringApplication实例初始化(准备工作)

当调用SpringApplication.run(...)时,首先会创建SpringApplication实例,完成一系列初始化操作:

复制代码
// 简化的初始化逻辑
public SpringApplication(Class<?>... primarySources) {
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 1. 推断应用类型(Servlet/Reactive/Native)
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 2. 加载初始化器(ApplicationContextInitializer)
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 3. 加载监听器(ApplicationListener)
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 4. 推断main方法所在的主类
    this.mainApplicationClass = deduceMainApplicationClass();
}

关键操作解析:

  • 推断应用类型 :通过类路径中是否存在ServletReactive相关类,判断是传统 Web 应用(SERVLET)、响应式 Web 应用(REACTIVE)还是非 Web 应用(NONE)。
  • 加载初始化器 :从META-INF/spring.factories文件中读取ApplicationContextInitializer实现类(用于初始化 Spring 上下文)。
  • 加载监听器 :同样从spring.factories中读取ApplicationListener实现类(用于监听启动过程中的事件,如环境准备完成、容器刷新等)。
阶段 2:run()方法执行(核心启动流程)

run()方法是启动的核心,包含 12 个关键步骤,按顺序执行如下:

步骤 1:启动计时器(记录启动时间)
复制代码
StopWatch stopWatch = new StopWatch();
stopWatch.start(); // 开始计时

用于统计应用启动总耗时,最终会在控制台输出(如Started MyApplication in 2.345 seconds)。

步骤 2:初始化运行监听器(SpringApplicationRunListeners

通过SpringFactoriesLoader加载SpringApplicationRunListener实现类(默认是EventPublishingRunListener),用于在启动各阶段发布事件(如ApplicationStartingEventApplicationEnvironmentPreparedEvent等),触发对应监听器的逻辑。

步骤 3:准备环境(Environment

创建并配置Environment(环境对象),包含:

  • 系统环境变量、JVM 参数、命令行参数(args)。
  • 配置文件(application.properties/application.yml)中的属性。
  • 激活的profiles(如dev/test/prod)。

过程:

  1. 为不同应用类型(Servlet/Reactive)创建对应Environment实例(如StandardServletEnvironment)。
  2. 加载配置文件:默认从classpath:classpath:/config/file:./file:./config/等路径读取。
  3. 处理命令行参数:将args中的参数(如--server.port=8081)添加到环境中,优先级最高。
步骤 4:打印 Banner(启动图标)

默认会在控制台打印 SpringBoot 的 Banner 图标(可通过spring.banner.location自定义,或设置spring.main.banner-mode=off关闭)。

步骤 5:创建ApplicationContext(Spring 容器)

根据应用类型创建对应的ApplicationContext(Spring 核心容器):

  • Servlet 应用AnnotationConfigServletWebServerApplicationContext
  • Reactive 应用AnnotationConfigReactiveWebServerApplicationContext
  • 非 Web 应用AnnotationConfigApplicationContext

ApplicationContext是 Spring 的 "大脑",负责管理 Bean 的生命周期、依赖注入等核心功能。

步骤 6:准备ApplicationContext(上下文预处理)

为容器设置环境、注册 Bean、应用初始化器等:

  1. 将步骤 3 中准备好的Environment设置到容器中。
  2. 调用所有ApplicationContextInitializerinitialize()方法,对容器进行自定义初始化(如添加属性源、修改配置等)。
  3. 发布ApplicationContextInitializedEvent事件,通知监听器容器已初始化。
  4. 注册主配置类:将@SpringBootApplication标注的类(如MyApplication)注册为 Spring 的配置类。
步骤 7:刷新ApplicationContext(容器核心初始化)

这是 Spring 容器的核心步骤(继承自 Spring 的AbstractApplicationContext),包含 Bean 的扫描、加载、实例化等关键操作,具体包括:

7.1 执行BeanFactory的前置处理

初始化容器的BeanFactory(如DefaultListableBeanFactory),用于管理 BeanDefinition(Bean 的元数据)。

7.2 执行BeanFactoryPostProcessor(Bean 工厂后置处理器)

最关键的是自动配置类的加载
@EnableAutoConfiguration通过@Import(AutoConfigurationImportSelector.class)导入自动配置类。AutoConfigurationImportSelector会从META-INF/spring.factories中读取EnableAutoConfiguration对应的配置类(如DataSourceAutoConfigurationWebMvcAutoConfiguration等),并根据@Conditional条件注解(如@ConditionalOnClass@ConditionalOnMissingBean)筛选出符合当前环境的配置类,注册为 BeanDefinition。

7.3 注册BeanPostProcessor(Bean 后置处理器)

用于在 Bean 实例化前后进行增强(如 AOP 代理、依赖注入等)。

7.4 初始化消息源(国际化支持)
7.5 初始化事件多播器(用于事件发布)
7.6 初始化容器特定 Bean(子类扩展点)

对 Web 应用而言,这里会触发嵌入式服务器的创建与启动
ServletWebServerApplicationContextonRefresh()方法中调用createWebServer(),根据类路径中的依赖(如spring-boot-starter-tomcat)创建对应的服务器(Tomcat/Undertow/Jetty),并绑定端口(默认 8080)。

7.7 注册监听器到容器
7.8 完成 BeanFactory 初始化(实例化所有非懒加载单例 Bean)

容器会遍历所有 BeanDefinition,实例化单例 Bean(@Lazy标注的除外),并执行依赖注入(@Autowired)、初始化方法(@PostConstructInitializingBean)等。

7.9 发布容器刷新完成事件(ContextRefreshedEvent
步骤 8:刷新后的操作
  • 清除缓存(如类加载缓存)。
  • 发布ApplicationStartedEvent事件(通知容器已刷新完成)。
步骤 9:执行Runner(自定义启动逻辑)

调用所有ApplicationRunnerCommandLineRunnerrun()方法,执行启动后的自定义逻辑(如加载初始数据、检查配置等):

  • ApplicationRunner:接收ApplicationArguments参数(解析后的命令行参数)。
  • CommandLineRunner:直接接收原始String[] args参数。
步骤 10:发布启动完成事件

发布ApplicationReadyEvent事件,通知应用已完全启动,可对外提供服务。

步骤 11:停止计时器
复制代码
stopWatch.stop(); // 停止计时
步骤 12:输出启动日志

打印启动成功日志,包含总耗时、活跃 Profiles 等信息(如Started MyApplication in 2.345 seconds (JVM running for 3.123))。

四、核心机制总结

  1. 自动配置 :通过@EnableAutoConfigurationspring.factories中的配置类,根据依赖和环境自动配置组件(如 DataSource、Web 服务器)。
  2. 嵌入式服务器:在容器刷新阶段自动创建并启动(如 Tomcat),无需手动部署到外部服务器。
  3. 事件驱动 :通过SpringApplicationRunListenerApplicationListener在启动各阶段发布事件,支持扩展(如自定义监听器处理特定阶段逻辑)。
  4. 简化配置:默认扫描路径、默认配置文件、默认 Bean 注册,减少手动配置。

五、流程图总结

复制代码
main() → SpringApplication实例化 → run()
  ↓
初始化监听器 → 准备环境(配置+参数) → 打印Banner → 创建ApplicationContext
  ↓
准备上下文(设置环境+注册配置类) → 刷新上下文(核心)
  ↓
  ├─ 加载自动配置类 → 注册BeanDefinition
  ├─ 实例化单例Bean → 依赖注入
  └─ 启动嵌入式服务器(如Tomcat)
  ↓
执行Runner → 发布启动完成事件 → 输出启动日志

通过这一系列流程,SpringBoot 实现了 "零配置(或极简配置)" 的快速启动,让开发者专注于业务逻辑而非框架配置。

相关推荐
用户685453759776919 分钟前
同步成本换并行度:多线程、协程、分片、MapReduce 怎么选才不踩坑
后端
javaTodo26 分钟前
Claude Code 记忆机制详解:从 CLAUDE.md 到 Auto Memory,六层体系全拆解
后端
LSTM971 小时前
使用 C# 和 Spire.PDF 从 HTML 模板生成 PDF 的实用指南
后端
JaguarJack1 小时前
为什么 PHP 闭包要加 static?
后端·php·服务端
BingoGo1 小时前
为什么 PHP 闭包要加 static?
后端
是糖糖啊1 小时前
OpenClaw 从零到一实战指南(飞书接入)
前端·人工智能·后端
百度Geek说2 小时前
基于Spark的配置化离线反作弊系统
后端
后端AI实验室2 小时前
用AI写代码,我差点把漏洞发上线:血泪总结的10个教训
java·ai
Java编程爱好者2 小时前
虚拟线程深度解析:轻量并发编程的未来趋势
后端