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

Spring Boot Starter Web 原理分析:从依赖到内嵌服务器的完整启动流程

Spring Boot 的spring-boot-starter-web是绝大多数 Java Web 开发者接触的第一个 Starter。一个注解、一个 main 方法,一个完整的 Web 服务器就跑起来了——DispatcherServlet 自动注册好了,Tomcat 内嵌启动好了,连 JSON 转换器都准备就绪。

这篇文章将从源码层面剖析这一切是如何发生的。


一、整体架构:starter-web 的核心四层

spring-boot-starter-web的设计遵循"开箱即用"的理念,其核心能力分布在四个层次:

层次载体职责
依赖管理层spring-boot-starter-webpom聚合所有必需依赖,统一版本
自动配置层spring-boot-autoconfigure提供条件化的配置类
容器抽象层spring-boot中的 WebServer 接口抽象 Tomcat/Jetty/Undertow
运行时层实际创建的内嵌容器实例监听端口、处理请求

下面从最外层的依赖开始,逐层深入。


二、第一层:起步依赖——Starter 如何"聚合"一切

2.1 一个依赖,全家桶到位

pom.xml中只需添加:

xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

这个依赖本身没有一行 Java 代码,它是一个纯粹的依赖聚合器。打开它的 pom 文件,会发现它引入了:

  • spring-boot-starter——核心场景启动器(包含自动配置基础)

  • spring-boot-starter-tomcat——默认内嵌容器

  • spring-webspring-webmvc——Spring MVC 框架

  • spring-boot-starter-json——Jackson JSON 处理

这种设计带来的好处是:版本由 Spring Boot 父项目统一仲裁,开发者无需关心版本兼容性

2.2 核心链路的依赖传递

text

spring-boot-starter-web ├── spring-boot-starter │ └── spring-boot-autoconfigure ← 自动配置核心包 ├── spring-boot-starter-tomcat │ └── tomcat-embed-core ← 内嵌 Tomcat ├── spring-webmvc ← Spring MVC └── spring-boot-starter-json └── jackson-databind ← JSON 支持

关键在于spring-boot-autoconfigure——它包含了 Spring Boot 官方提供的所有自动配置类(约 100+ 个),其中就包括 Web MVC 和嵌入式容器的配置。


三、第二层:自动配置——条件化装配的核心机制

3.1 @SpringBootApplication 的"三合一"身份

主程序类的@SpringBootApplication是一个组合注解:

java

@SpringBootConfiguration // 本质是 @Configuration @EnableAutoConfiguration // 开启自动配置 ← 核心 @ComponentScan // 组件扫描 public @interface SpringBootApplication {}

其中最关键的是@EnableAutoConfiguration。它通过@Import(AutoConfigurationImportSelector.class),在启动时会批量导入自动配置类

3.2 spring.factories 与 AutoConfiguration.imports

Spring Boot 2.7 之后,自动配置类的声明从META-INF/spring.factories迁移到了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

这个文件里列出了所有候选配置类的全限定名:

text

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration ...

Spring Boot 启动时会读取这个文件,将列出的类全部加载——注意是"加载到内存",而非"直接生效"。是否真正生效,取决于类上的条件注解

3.3 条件注解:按需生效的"开关"

WebMvcAutoConfiguration为例:

java

@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) @ConditionalOnWebApplication(type = Type.SERVLET) @AutoConfigureAfter(DispatcherServletAutoConfiguration.class) public class WebMvcAutoConfiguration { // 配置 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 等 }

这些条件注解的含义:

  • @ConditionalOnClass(Servlet.class):类路径下必须有Servlet.class(web 项目天然满足)

  • @ConditionalOnWebApplication:必须是 Servlet 类型的 Web 应用

  • @AutoConfigureAfter:在DispatcherServletAutoConfiguration之后执行,保证执行顺序

再看DispatcherServletAutoConfiguration内部:

java

@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) @EnableConfigurationProperties(WebMvcProperties.class) public class DispatcherServletAutoConfiguration { @Configuration @Conditional(DefaultDispatcherServletCondition.class) @EnableConfigurationProperties(WebMvcProperties.class) protected static class DispatcherServletConfiguration { @Bean public DispatcherServlet dispatcherServlet() { return new DispatcherServlet(); } } }

只要条件满足,Spring Boot 就会自动将 DispatcherServlet 注册到容器中,开发者无需再写 web.xml 或 @Bean 配置。

3.4 属性绑定:配置文件如何注入

自动配置类通常配合@EnableConfigurationProperties使用,将配置文件中的属性绑定到 POJO:

java

@ConfigurationProperties(prefix = "server") public class ServerProperties { private Integer port = 8080; // 默认端口 // getter/setter }

这样,application.yml中的server.port=9090就能被自动读取并应用。


四、第三层:内嵌容器的装配与启动

4.1 容器工厂的自动配置

嵌入式 Web 容器的装配由ServletWebServerFactoryAutoConfiguration负责。它会根据 classpath 中的依赖,创建对应的工厂 bean:

java

@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @EnableConfigurationProperties(ServerProperties.class) public class ServletWebServerFactoryAutoConfiguration { // 导入具体的容器配置 }

这个配置类通过内部@Import导入了三种容器的配置类:

text

@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, // Tomcat ServletWebServerFactoryConfiguration.EmbeddedJetty.class, // Jetty ServletWebServerFactoryConfiguration.EmbeddedUndertow.class // Undertow })

Tomcat 是默认的,因为spring-boot-starter-web中引入了spring-boot-starter-tomcat。如果希望切换到 Jetty,只需排除 Tomcat 并引入 Jetty 依赖即可:

xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>

4.2 容器的创建时机:onRefresh()

内嵌容器的创建发生在 Spring 容器的refresh()过程中。AnnotationConfigServletWebServerApplicationContext(实际使用的上下文类)继承自ServletWebServerApplicationContext,后者重写了onRefresh()方法:

java

// ServletWebServerApplicationContext.java @Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); // 关键:创建 Web 服务器 } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { // 从容器中获取 ServletWebServerFactory(即 TomcatServletWebServerFactory) ServletWebServerFactory factory = getWebServerFactory(); // 创建 WebServer 实例 this.webServer = factory.getWebServer(getSelfInitializer()); // 触发 ServletContext 初始化事件 getSelfInitializer().onStartup(this.webServer.getServletContext()); }

factory.getWebServer()方法的执行流程:

  1. 创建 Tomcat 实例

  2. 创建 Connector(默认端口 8080,可通ServerProperties配置覆盖)

  3. 配置 Engine、Host、Context

  4. 返回TomcatWebServer对象(此时容器尚未启动,只完成了初始化)

4.3 容器的启动时机:finishRefresh()

容器在onRefresh()中创建完成,但真正启动是在finishRefresh()阶段:

java

// ServletWebServerApplicationContext.java @Override protected void finishRefresh() { super.finishRefresh(); startWebServer(); // 启动 Web 服务器 publishEvent(new ServletWebServerInitializedEvent(this.webServer, this)); } private void startWebServer() { WebServer webServer = getWebServer(); if (webServer != null) { webServer.start(); // 启动 Tomcat } }

进入TomcatWebServer.start()内部:

java

// TomcatWebServer.java public void start() throws WebServerException { synchronized (this.monitor) { if (this.started) { return; } try { addPreviouslyRemovedConnectors(); // 启动 Tomcat 的核心方法 Tomcat tomcat = getTomcat(); tomcat.start(); // 检查 Connector 是否启动成功 rethrowDeferredStartupExceptions(); // 启动一个后台线程等待 Connector 初始化完成 startDaemonAwaitThread(); this.started = true; } catch (Exception ex) { // 异常处理 } } }

此时控制台会输出经典的日志:

text

Tomcat started on port(s): 8080 (http) with context path '' Started Application in 2.345 seconds (JVM running for 2.678)

至此,完整的 Web 服务器启动完毕。


五、完整时序图

为了更直观地理解整个过程,以下是关键步骤的时序:

说明:该图展示的是 Spring Boot 启动过程中,从 main 方法开始,到自动配置类加载、容器创建、Tomcat 启动的完整调用链路。


六、切换到 Jetty/Undertow 的原理

理解了默认流程后,切换容器的原理就很清晰了:

  1. ServletWebServerFactoryAutoConfiguration导入了三种容器的配置类

  2. 每种容器的配置类上都带有@ConditionalOnClass注解

  3. 当排除了spring-boot-starter-tomcat后,Tomcat 相关的类(如TomcatTomcatServletWebServerFactory)不再存在于 classpath 中

  4. 因此EmbeddedTomcat配置类不生效,而 Jetty 的配置类因引入了spring-boot-starter-jetty而生效

本质是基于 classpath 的条件化配置在发挥作用。


七、总结

spring-boot-starter-web的设计体现了 Spring Boot 的核心哲学:

  1. 依赖聚合:一个 Starter 聚合了 Web 开发所需的所有依赖,版本由父项目统一仲裁

  2. 条件装配:自动配置类通过@ConditionalOnXxx实现按需生效,避免过度加载

  3. 生命周期钩子:利用 Spring 容器的onRefresh()finishRefresh()模板方法,在恰当的时机创建和启动内嵌容器

  4. 工厂抽象:通过ServletWebServerFactory接口统一不同容器的创建逻辑,切换只需改依赖

理解这四层设计,不仅能用好 Web Starter,也能为自定义 Starter提供清晰的参考模式——这正是 Spring Boot 能够成为 Java 生态基石的底层逻辑。

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

相关文章:

  • 空间折叠算法验证:软件测试视角下的原理、挑战与实践路径
  • 抖音批量下载器终极指南:3行命令实现无水印视频自动化采集
  • 基于图扑软件 HT 2.5D 组态可视化技术的场景实现
  • 2026制造业协同管理平台选型避坑指南
  • 如何快速掌握实时数字人技术:面向开发者的完整指南
  • 反物质存储风险:从技术挑战到安全哲学的深度解析
  • CSDN格式解析真不错
  • RT-thread 链接阶段如何把段排列到内存里,然后运行阶段如何遍历这些函数指针并调用。
  • 字符缩到0.8mm板子丑到没法看!忽略的丝印美学
  • mini-job极简分布式延迟任务队列 — 基于 Redis,支持 Cron 周期任务、异步协程和多执行器
  • 【论文阅读】AWR:Simple and scalable off-policy RL
  • AI 赋能研发:现代开发者的效率进阶与工程化落地实践
  • 思源黑体TTF:7种字重完美解决多语言排版难题
  • 二向箔压缩测试:从宇宙规律武器到软件测试范式的跨界思考
  • AWS DevOps Agent 实测:AI 自主运维从告警到根因报告的完整技术路径
  • 【Hot 100 刷题计划】 LeetCode 23. 合并 K 个升序链表 | C++ 顺序合并
  • MusicFree插件完全指南:打造你的个性化跨平台音乐中心
  • 推荐2款无需安装实用软件,桌面图标整理设置,简真是Windows神器!
  • 解码AI用户心智,筑牢可信GEO根基——悠易科技深度参与《中国AI用户态度与行为研究报告(2026)》发布会
  • 从Jupyter Notebook到生产API,Docker AI Toolkit 2026全流程自动化部署(含OpenTelemetry埋点、Prometheus监控集成脚本)
  • GitHub中文界面大改造:3分钟让英文GitHub秒变中文版
  • XPath Helper Plus:3分钟掌握网页元素精准定位的终极指南
  • WASM容器化部署为何突然爆发?,2026全球Top 12边缘AI项目验证的Docker+WASI运行时架构演进路径
  • 别再为低价忽视丝印规格
  • 如何3分钟解锁Wallpaper Engine所有壁纸素材?RePKG工具终极指南
  • Ostrakon-VL-8B数据预处理详解:餐饮图像清洗与标注规范
  • 从ArrayList到VectorSpecies:Java向量化开发全流程拆解,含GraalVM AOT+Linux perf火焰图调优实战
  • MCP Server 接口开发规范与最佳实践
  • QQ音乐加密文件终极解密指南:3步解锁你的音乐宝藏
  • 忍者像素绘卷Codex使用技巧:利用AI编程助手快速开发模型调用脚本