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中只需添加xmldependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency这个依赖本身没有一行 Java 代码它是一个纯粹的依赖聚合器。打开它的 pom 文件会发现它引入了spring-boot-starter——核心场景启动器包含自动配置基础spring-boot-starter-tomcat——默认内嵌容器spring-web和spring-webmvc——Spring MVC 框架spring-boot-starter-json——Jackson JSON 处理这种设计带来的好处是版本由 Spring Boot 父项目统一仲裁开发者无需关心版本兼容性。2.2 核心链路的依赖传递textspring-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是一个组合注解javaSpringBootConfiguration // 本质是 Configuration EnableAutoConfiguration // 开启自动配置 ← 核心 ComponentScan // 组件扫描 public interface SpringBootApplication {}其中最关键的是EnableAutoConfiguration。它通过Import(AutoConfigurationImportSelector.class)在启动时会批量导入自动配置类。3.2 spring.factories 与 AutoConfiguration.importsSpring Boot 2.7 之后自动配置类的声明从META-INF/spring.factories迁移到了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。这个文件里列出了所有候选配置类的全限定名textorg.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为例javaConfiguration(proxyBeanMethods false) ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) ConditionalOnWebApplication(type Type.SERVLET) AutoConfigureAfter(DispatcherServletAutoConfiguration.class) public class WebMvcAutoConfiguration { // 配置 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 等 }这些条件注解的含义ConditionalOnClass(Servlet.class)类路径下必须有Servlet.classweb 项目天然满足ConditionalOnWebApplication必须是 Servlet 类型的 Web 应用AutoConfigureAfter在DispatcherServletAutoConfiguration之后执行保证执行顺序再看DispatcherServletAutoConfiguration内部javaConfiguration(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使用将配置文件中的属性绑定到 POJOjavaConfigurationProperties(prefix server) public class ServerProperties { private Integer port 8080; // 默认端口 // getter/setter }这样application.yml中的server.port9090就能被自动读取并应用。四、第三层内嵌容器的装配与启动4.1 容器工厂的自动配置嵌入式 Web 容器的装配由ServletWebServerFactoryAutoConfiguration负责。它会根据 classpath 中的依赖创建对应的工厂 beanjavaConfiguration(proxyBeanMethods false) AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) ConditionalOnClass(ServletRequest.class) EnableConfigurationProperties(ServerProperties.class) public class ServletWebServerFactoryAutoConfiguration { // 导入具体的容器配置 }这个配置类通过内部Import导入了三种容器的配置类textImport({ 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 依赖即可xmldependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId /exclusion /exclusions /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jetty/artifactId /dependency4.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()方法的执行流程创建 Tomcat 实例创建 Connector默认端口 8080可通ServerProperties配置覆盖配置 Engine、Host、Context返回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) { // 异常处理 } } }此时控制台会输出经典的日志textTomcat 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 的原理理解了默认流程后切换容器的原理就很清晰了ServletWebServerFactoryAutoConfiguration导入了三种容器的配置类每种容器的配置类上都带有ConditionalOnClass注解当排除了spring-boot-starter-tomcat后Tomcat 相关的类如Tomcat、TomcatServletWebServerFactory不再存在于 classpath 中因此EmbeddedTomcat配置类不生效而 Jetty 的配置类因引入了spring-boot-starter-jetty而生效本质是基于 classpath 的条件化配置在发挥作用。七、总结spring-boot-starter-web的设计体现了 Spring Boot 的核心哲学依赖聚合一个 Starter 聚合了 Web 开发所需的所有依赖版本由父项目统一仲裁条件装配自动配置类通过ConditionalOnXxx实现按需生效避免过度加载生命周期钩子利用 Spring 容器的onRefresh()和finishRefresh()模板方法在恰当的时机创建和启动内嵌容器工厂抽象通过ServletWebServerFactory接口统一不同容器的创建逻辑切换只需改依赖理解这四层设计不仅能用好 Web Starter也能为自定义 Starter提供清晰的参考模式——这正是 Spring Boot 能够成为 Java 生态基石的底层逻辑。