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

SpringBoot 应用启动流程:从启动到 Web 容器初始化

上一章我们讲解了 SpringBoot 事件机制,其实 SpringBoot 自身的启动全流程,就是一场“事件驱动”的完整实践。很多同学日常开发中,只知道“运行 main 方法就能启动项目”,但对其底层启动逻辑一知半解——比如 main 方法到底做了什么?Spring 容器是何时初始化的?Web 容器(Tomcat、Jetty)又是如何被启动的?配置文件是何时加载的?

理解 SpringBoot 启动流程,不仅能帮我们快速定位启动报错(如配置加载失败、Bean 初始化异常、Web 容器启动失败等),更是面试高频考点SpringBoot 启动流程是什么?SpringBoot 如何集成 Web 容器?)。

一、启动入口:main 方法的核心作用

SpringBoot 应用的启动入口,就是我们最熟悉的main方法,通常写在标注了@SpringBootApplication注解的启动类中。看似简单的一行代码,背后隐藏着整个启动流程的入口逻辑。

1. 启动类示例

// 启动类:标注 @SpringBootApplication,作为应用启动入口 @SpringBootApplication public class SpringBootDemoApplication { // 启动入口:main 方法 public static void main(String[] args) { // 核心代码:启动 SpringBoot 应用 SpringApplication.run(SpringBootDemoApplication.class, args); } }

2. 核心入口:SpringApplication.run() 方法

整个 SpringBoot 启动流程,都是从SpringApplication.run()方法开始的。该方法看似简单,实则做了两件核心事情:

  • • 创建SpringApplication实例:初始化应用的核心配置(如判断应用类型、加载初始化器、监听器等);

  • • 调用SpringApplication.run()重载方法:执行具体的启动流程(环境准备、上下文初始化、Web 容器启动等)。

3. 源码解析:SpringApplication 实例创建

当我们调用SpringApplication.run(SpringBootDemoApplication.class, args)时,首先会创建SpringApplication实例,其构造方法会完成一系列初始化操作:

// SpringApplication 构造方法(简化版,核心逻辑) public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; // 1. 校验启动类(primarySources),不能为空 Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 2. 判断应用类型(至关重要:是 Web 应用还是非 Web 应用) this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 3. 加载 Spring 内置的初始化器(ApplicationContextInitializer) setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 4. 加载 Spring 内置的监听器(ApplicationListener) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 5. 推断 main 方法所在的启动类(即我们写的 SpringBootDemoApplication) this.mainApplicationClass = deduceMainApplicationClass(); }
注意事项
  • 应用类型判断(webApplicationType):SpringBoot 会根据类路径中是否存在 Web 相关依赖(如 Tomcat、Spring Web 依赖),自动判断应用类型:

    • SERVLET:传统 Web 应用(依赖 spring-webmvc、tomcat 等),会启动 Servlet 容器(Tomcat 默认);

    • REACTIVE:响应式 Web 应用(依赖 spring-webflux),会启动响应式 Web 容器;

    • NONE:非 Web 应用(无 Web 依赖),不启动任何 Web 容器。

  • 初始化器(ApplicationContextInitializer):用于在 Spring 容器(ApplicationContext)初始化前,对容器进行自定义配置(如设置环境变量、添加自定义 Bean 定义等),Spring 内置了多个初始化器,也支持自定义。

  • 监听器(ApplicationListener):加载 Spring 内置的监听器(如监听启动事件、上下文刷新事件等),这些监听器会在启动流程的不同节点被触发,驱动后续操作(比如上一章讲的 ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent 等)。

二、核心启动流程:从初始化到应用就绪

创建完SpringApplication实例后,会调用其run()方法,这是整个启动流程的核心。我们将其拆解为 8 个关键步骤,每一步都对应启动流程的一个核心操作,结合源码和实战细节,逐个解析。

步骤1:启动计时与发布“应用启动开始事件”

启动流程的第一步,是初始化启动计时器(用于统计整个启动耗时),并发布ApplicationStartingEvent(应用启动开始事件)——这是 SpringBoot 启动过程中最早触发的事件,此时 Spring 容器还未初始化,环境配置也未准备。

// SpringApplication.run() 方法核心逻辑(简化版) public ConfigurableApplicationContext run(String... args) { // 1. 初始化启动计时器(统计启动耗时) StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 开始计时 // 2. 初始化应用上下文(ApplicationContext)和异常报告器 ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // 3. 配置系统属性(如 headless 模式,避免图形化依赖) configureHeadlessProperty(); // 4. 获取 SpringApplication 监听器(之前构造方法中加载的) SpringApplicationRunListeners listeners = getRunListeners(args); // 5. 发布 ApplicationStartingEvent(应用启动开始事件) listeners.starting(); try { // 后续步骤... } catch (Throwable ex) { // 异常处理... } }

✅ 注意事项:此时监听器可以接收ApplicationStartingEvent,但由于容器和环境未初始化,无法进行依赖注入、配置加载等操作,通常用于启动前的系统参数初始化。

步骤2:准备环境(Environment)

环境准备是启动流程的核心步骤之一,SpringBoot 会创建并配置Environment(环境对象),加载所有配置信息(配置文件、系统环境变量、命令行参数等),并发布ApplicationEnvironmentPreparedEvent(环境准备完成事件)。

环境准备的核心操作的包括:

  1. 1. 创建环境对象:根据应用类型(SERVLET/REACTIVE/NONE),创建对应的环境对象(如StandardServletEnvironment用于 Servlet 应用);

  2. 2. 加载配置源:依次加载系统环境变量、系统属性、命令行参数、application 配置文件(application.properties/yml)、自定义配置等;

  3. 3. 配置 Profiles:激活指定的 Profiles(如 dev、test、prod),加载对应环境的配置;

  4. 4. 发布事件:环境准备完成后,发布ApplicationEnvironmentPreparedEvent,此时监听器可以获取并修改环境配置(比如动态添加配置源)。

// 环境准备核心代码(简化版) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 准备环境:加载配置、激活 Profiles 等 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 配置忽略的 Bean 信息 configureIgnoreBeanInfo(environment);

✅ 注意事项:如果启动时报“配置文件加载失败”“配置项不存在”等错误,问题通常出在这一步——比如配置文件路径错误、配置项拼写错误、Profiles 激活错误等。

步骤3:打印 Banner

环境准备完成后,SpringBoot 会打印我们熟悉的 Banner(启动图标),这一步是可选的,我们可以通过配置spring.banner.enabled=false关闭 Banner,也可以自定义 Banner(如在 resources 目录下放置 banner.txt 文件)。

// 打印 Banner 核心代码 Banner printedBanner = printBanner(environment);

✅ 小技巧:自定义 Banner 时,可以通过在线工具生成字符画,放入 banner.txt 中,启动时就会显示自定义的图标,增加项目辨识度。

步骤4:创建并初始化 Spring 应用上下文(ApplicationContext)

应用上下文(ApplicationContext)是 Spring 容器的核心,负责管理所有 Bean 的生命周期(创建、初始化、销毁)、依赖注入、事件发布等。这一步会根据应用类型,创建对应的上下文实例,并完成初始化。

核心操作:
  1. 1. 创建上下文:根据应用类型选择对应的上下文(Servlet 应用对应AnnotationConfigServletWebServerApplicationContext,非 Web 应用对应AnnotationConfigApplicationContext);

  2. 2. 配置上下文:将环境对象(Environment)、初始化器(ApplicationContextInitializer)、监听器(ApplicationListener)等注入上下文;

  3. 3. 应用初始化器:调用所有初始化器的initialize()方法,对上下文进行自定义配置;

  4. 4. 发布事件:上下文初始化完成后,会发布相关事件(如ApplicationContextInitializedEvent)。

// 创建并初始化上下文核心代码 context = createApplicationContext(); // 准备上下文:注入环境、初始化器、监听器等 prepareContext(context, environment, listeners, applicationArguments, printedBanner);

✅ 注意事项:上下文的创建是 Spring 容器启动的核心,后续的 Bean 加载、依赖注入,都依赖于这个上下文对象。如果启动时报“Bean 找不到”“依赖注入失败”,大概率是上下文初始化异常导致的。

步骤5:刷新应用上下文

上下文初始化完成后,会调用refresh()方法,这是整个 Spring 容器初始化的核心操作,也是最复杂的一步。refresh()方法会完成 Bean 的扫描、加载、初始化、依赖注入,以及 Spring 内置功能的初始化(如 AOP、事务管理等)。

我们重点关注与启动流程相关的核心操作(完整的 refresh 流程可参考 Spring 核心源码):

  1. 1. 刷新前准备:初始化上下文的生命周期处理器、验证环境配置等;

  2. 2. Bean 定义扫描:扫描启动类所在包及其子包下,所有标注了@Component@Service@Repository@Controller等注解的类,将其注册为 Bean 定义;

  3. 3. Bean 初始化:实例化所有非懒加载的 Bean,完成依赖注入(@Autowired)、初始化方法执行(@PostConstruct);

  4. 4. 初始化 Spring 内置功能:如 AOP 代理、事务管理器、事件广播器等;

  5. 5. 发布ContextRefreshedEvent(上下文刷新完成事件):此时所有 Bean 已初始化完成,Spring 容器正式可用。

// 刷新上下文核心代码 refreshContext(context);

✅ 注意事项:这一步是启动报错的高频区域——比如 Bean 依赖循环、Bean 初始化方法抛出异常、AOP 配置错误等,都会导致 refresh 失败,从而启动失败。

步骤6:启动 Web 容器

如果是 Web 应用(应用类型为 SERVLET 或 REACTIVE),上下文刷新完成后,会启动对应的 Web 容器(默认 Tomcat),并将 Web 应用部署到容器中,使其能够接收 HTTP 请求。这一步也是 SpringBoot 实现“嵌入式 Web 容器”的核心。

核心逻辑(以 Tomcat 为例):
  1. 1. 判断应用类型:如果是 Web 应用,上下文会包含ServletWebServerFactory(Web 容器工厂),用于创建 Web 容器;

  2. 2. 创建 Web 容器:通过ServletWebServerFactory创建 Tomcat 容器实例,配置容器参数(如端口号、上下文路径等,读取 application 配置中的server.port等参数);

  3. 3. 部署 Web 应用:将 SpringMVC 的DispatcherServlet注册到 Tomcat 容器中,负责接收和分发 HTTP 请求;

  4. 4. 启动 Web 容器:调用 Tomcat 容器的启动方法,绑定端口,开始监听 HTTP 请求;

  5. 5. 发布事件:Web 容器启动完成后,发布ServletWebServerInitializedEvent(Web 容器初始化完成事件)。

// 启动 Web 容器核心代码(简化版) private void finishRefresh() { // 初始化生命周期处理器 initLifecycleProcessor(); // 触发所有 Bean 的生命周期方法(start) getLifecycleProcessor().onRefresh(); // 发布上下文刷新完成事件 publishEvent(new ContextRefreshedEvent(this)); // 如果是 Web 应用,启动 Web 容器 WebServer webServer = startWebServer(); if (webServer != null) { // 发布 Web 容器初始化完成事件 publishEvent(new ServletWebServerInitializedEvent(webServer, this)); } }
注意事项:
  • 嵌入式 Web 容器:SpringBoot 之所以无需手动部署到外部 Tomcat,是因为它将 Tomcat、Jetty 等 Web 容器嵌入到应用中,通过代码启动容器,简化部署流程;

  • 端口配置:Web 容器的端口默认是 8080,可通过server.port配置修改;如果端口被占用,会抛出BindException,需修改端口或释放占用端口;

  • 容器切换:默认使用 Tomcat,可通过排除 Tomcat 依赖、引入 Jetty 或 Undertow 依赖,实现 Web 容器的切换(如微服务场景中,Undertow 性能更优)。

步骤7:执行自定义 Runner(初始化任务)

Web 容器启动完成后,SpringBoot 会执行自定义的初始化任务——即实现了CommandLineRunnerApplicationRunner接口的 Bean。这是我们在应用启动完成后,执行自定义逻辑(如加载缓存、初始化字典、发送启动通知)的常用方式。

两种 Runner 对比:
  • CommandLineRunner:接收命令行参数(String[] args),参数是原始的命令行字符串;

  • ApplicationRunner:接收ApplicationArguments对象,可更方便地获取命令行参数(如选项参数、非选项参数)。

// 示例1:实现 CommandLineRunner @Component public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 启动完成后执行的逻辑(如加载缓存) System.out.println("CommandLineRunner 执行:应用启动完成,加载缓存中..."); } } // 示例2:实现 ApplicationRunner @Component public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { // 获取命令行参数(如 --name=test) List<String> nameArgs = args.getOptionValues("name"); System.out.println("ApplicationRunner 执行:命令行参数 name=" + nameArgs); } }

✅ 注意事项:如果有多个 Runner,可通过@Order注解指定执行顺序(值越小,优先级越高)。

步骤8:发布“应用启动完成事件”,启动结束

所有初始化操作(上下文刷新、Web 容器启动、Runner 执行)完成后,SpringBoot 会发布ApplicationStartedEvent(应用启动完成事件)和ApplicationReadyEvent(应用就绪事件),并停止启动计时器,打印启动耗时。

  • ApplicationStartedEvent:应用已启动完成,所有 Bean 已初始化,Web 容器已启动,但服务尚未完全就绪;

  • ApplicationReadyEvent:应用完全就绪,可正常接收和处理 HTTP 请求(这是启动流程的最后一个事件)。

// 启动完成核心代码(简化版) listeners.started(context); // 执行 Runner callRunners(context, applicationArguments); // 发布应用就绪事件 listeners.ready(context); // 停止计时器,打印启动耗时 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); }

✅ 最终效果:控制台会打印“Started SpringBootDemoApplication in X.X seconds (JVM running for X.X)”,表示应用启动完成,可正常提供服务。

三、Web 容器初始化的详细拆解

对于 Web 应用来说,Web 容器的初始化是启动流程中最关键的环节之一,也是面试中高频提问的点。我们以默认的 Tomcat 容器为例,详细拆解其初始化和启动流程,搞懂“SpringBoot 如何启动 Tomcat 并部署应用”。

1. Web 容器工厂(ServletWebServerFactory)

SpringBoot 通过ServletWebServerFactory(Web 容器工厂)创建 Web 容器实例,不同的 Web 容器对应不同的工厂类:

  • • Tomcat:TomcatServletWebServerFactory(默认);

  • • Jetty:JettyServletWebServerFactory

  • • Undertow:UndertowServletWebServerFactory

SpringBoot 会根据类路径中引入的 Web 容器依赖,自动配置对应的工厂类(自动配置机制),无需手动配置。

2. Tomcat 容器初始化步骤

  1. 1.创建 Tomcat 实例:通过TomcatServletWebServerFactory创建 Tomcat 实例,设置默认的基础配置(如工作目录、连接器等);

  2. 2.配置连接器(Connector):连接器负责监听 HTTP 请求,默认使用 8080 端口,支持 HTTP/1.1 协议,可通过server.portserver.tomcat.connector.*等配置修改;

  3. 3.创建 Web 应用上下文(TomcatWebApplicationContext):用于部署 Spring Web 应用,关联 Spring 的应用上下文(ApplicationContext);

  4. 4.注册 DispatcherServlet:将 SpringMVC 的核心DispatcherServlet注册到 Tomcat 中,设置其映射路径(默认是 “/”),负责接收所有 HTTP 请求并分发;

  5. 5.启动 Tomcat 容器:调用 Tomcat 实例的start()方法,启动连接器和引擎,开始监听 HTTP 请求;

  6. 6.绑定端口:将 Tomcat 连接器绑定到指定端口(默认 8080),如果端口被占用,启动失败并抛出异常。

3. 修改 Web 容器配置

日常开发中,我们常需要修改 Web 容器的配置(如端口、工作目录、连接超时等),主要有两种方式:

方式1:通过 application 配置文件
# Tomcat 核心配置 server.port=8081 # 修改端口为 8081 server.tomcat.uri-encoding=utf-8 # 设置 URI 编码 server.tomcat.connection-timeout=30000 # 连接超时时间(毫秒) server.tomcat.basedir=./tomcat-work # Tomcat 工作目录 server.tomcat.max-threads=200 # 最大线程数 server.tomcat.min-spare-threads=5 # 最小空闲线程数
方式2:通过自定义 ServletWebServerFactory

适用于更复杂的配置(如添加自定义连接器、修改引擎配置等):

@Configuration public class TomcatConfig { @Bean public ServletWebServerFactory servletWebServerFactory() { // 创建 Tomcat 容器工厂 TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); // 修改端口 factory.setPort(8081); // 配置连接器 factory.addAdditionalTomcatConnectors(createConnector()); return factory; } // 自定义连接器(如添加 HTTPS 连接器) private Connector createConnector() { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); connector.setPort(8443); // HTTPS 端口 connector.setScheme("https"); connector.setSecure(true); // 配置 SSL 证书(省略细节) return connector; } }

4. 常见 Web 容器启动失败原因

  • 端口被占用:最常见原因,通过netstat -ano | findstr 8080(Windows)或lsof -i:8080(Linux)查看端口占用情况,释放端口或修改端口;

  • SSL 配置错误:如果配置了 HTTPS,证书路径错误、证书过期会导致 Tomcat 启动失败;

  • Web 依赖冲突:如同时引入 Tomcat 和 Jetty 依赖,导致容器工厂冲突;

  • 端口号非法:端口号需在 1-65535 之间,超出范围会启动失败。

四、启动流程核心机制

理解启动流程的同时,还要掌握其背后的核心机制,这些是面试中高频提问的重点,也是区分“新手”和“老手”的关键。

1. 事件驱动机制

SpringBoot 启动流程的每一个关键节点,都通过发布内置事件驱动后续操作,核心事件按触发顺序如下(必记):

  1. 1. ApplicationStartingEvent:应用启动开始(最早);

  2. 2. ApplicationEnvironmentPreparedEvent:环境准备完成;

  3. 3. ApplicationContextInitializedEvent:应用上下文初始化完成;

  4. 4. ContextRefreshedEvent:应用上下文刷新完成(Bean 全部初始化);

  5. 5. ServletWebServerInitializedEvent:Web 容器初始化完成;

  6. 6. ApplicationStartedEvent:应用启动完成;

  7. 7. ApplicationReadyEvent:应用就绪(最后)。

2. 自动配置机制

SpringBoot 启动流程中,很多配置(如 Web 容器工厂、环境配置、Bean 扫描等)都是通过自动配置机制完成的,无需手动配置。核心是@EnableAutoConfiguration注解(被@SpringBootApplication注解包含),通过扫描META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,加载所有自动配置类。

比如:Web 容器的自动配置(ServletWebServerFactoryAutoConfiguration)、SpringMVC 的自动配置(WebMvcAutoConfiguration)等,都是通过自动配置机制加载的。

3. 应用上下文分层

SpringBoot 启动过程中,会创建两个核心上下文,分工明确:

  • 启动上下文(BootstrapContext):用于加载启动过程中需要的核心配置(如配置中心的配置、加密解密配置等),优先级高于应用上下文;

  • 应用上下文(ApplicationContext):用于管理应用中的所有 Bean、依赖注入、事件发布等,是日常开发中接触最多的上下文。

掌握本章内容,不仅能快速定位启动过程中的各种问题,还能从容应对面试中的相关提问,更能深入理解 SpringBoot“简化配置、快速开发”的核心思想。后续我们会讲解启动流程中的异常排查技巧,帮助大家在实际开发中快速解决启动问题。

http://www.jsqmd.com/news/637769/

相关文章:

  • 【工业级AIAgent仿真底座】:基于Docker+Kubernetes+gymnasium的可复现、可审计、可压测环境搭建全链路
  • 从零搭建高性能BitTorrent Tracker:xbt-Tracker与Transmission全流程指南
  • 双非本科入行AI Agent:我是怎么跑通这条路的
  • 45、如何理解和实现递归?数组扁平化里递归有什么缺陷?
  • LightOnOCR-2-1B手把手教学:从零开始,打造你的智能文字提取工具
  • RobotStudio多版本共存避坑指南:5.0/6.0/2019版如何和平共处?
  • 智能优化算法专题(7)【讲解+报告】基于PID控制与模糊PID控制搭建一阶倒立摆仿真(在线整定PID参数)-对比小车位移与摆杆角度
  • 2026年4月洁净手术室厂商推荐,弥散供氧/厂房净化/供氧设备带/医用气体/集中供氧/无菌手术室,洁净手术室商家怎么选择 - 品牌推荐师
  • GX0011单线脉冲温度传感器实战:从NTC替代到STM32驱动,实现低功耗多点测温
  • 杭州专业WordPress模板开发服务商
  • 安科瑞AIM-T系列工业IT绝缘监测及故障定位解决方案为关键供电场所筑牢安全防线
  • RTX 4090高效利用:Anything to RealCharacters 2.5D转真人引擎Xformers加速教程
  • AI Agent求职被拒最常见的原因是什么
  • 分享 种 .NET 桌面应用程序自动更新解决方案
  • DFT笔记39
  • 2026届最火的降重复率方案推荐榜单
  • 2026年4月沃伦勒夫运动手环推荐,沃伦勒夫/卫康沃伦勒夫/沃伦勒夫Warrenslove,沃伦勒夫运动手环可靠吗 - 品牌推荐师
  • 机器学习与深度学习的区别是什么?如何选择研究方向?(收藏版)
  • 落子珠江,新址启航|安托广州子公司盛大开业
  • 2026 年 Intel 酷睿 Ultra 平台装机:华硕主板全系列专业主板测评与精准选购指南
  • IndexTTS2 V23实战体验:上传音频秒变同款语气,效果惊艳
  • 手把手教你用F1C200s驱动正点原子7寸LCD屏:完整配置流程与LVGL测试
  • 2026年比较好的国内道路路灯/国内庭院灯/国内双头壁灯/国内瓦楞灯深度厂家推荐 - 品牌宣传支持者
  • LSTM多输入多输出预测模型技术功能说明
  • 实测智码方舟:花100元用AI生成毕设代码,完整记录从注册到答辩的全过程
  • 碧蓝航线Live2D提取完全指南:5分钟掌握角色动画资源获取
  • 3个步骤掌握OpCore Simplify:让黑苹果配置从复杂到简单的革命性工具
  • CV算法工程师求职全攻略:25个知识点帮你通关面试
  • 2026年靠谱的国内楼体亮化灯/国内草坪灯/国内灯具优质公司推荐 - 行业平台推荐
  • AnimateDiff文生视频:8G显存也能玩,生成自然风光瀑布流动视频