
1. 问题现象与影响范围先描述一下这个报错的样子。如果你在 IDEA 里启动一个 Spring Boot 项目控制台刷出类似这样的堆栈*************************** APPLICATION FAILED TO START *************************** Description: Web application could not be started as there was no ServletWebServerFactory defined in the application context. Action: Consider adding an explicit Bean definition of ServletWebServerFactory, or adding spring-boot-starter-web to your classpath.还没等你看到 Spring Boot 的 Logo 和端口号应用就退出。这种情况在 Spring Boot 2.x 项目中非常典型但不少人在第一次遇到时会被ServletWebServerFactory这个类名吓住以为是自己写的某个配置写错了或者 IDEA 的 Run Configuration 配错了。实际上这个错误的核心含义是Spring Boot 没有在应用上下文里找到一个可用于创建内嵌 Web 服务器的工厂 Bean。换句话说Spring Boot 根本不知道你要跑的是一个 Web 应用。而 Spring Boot 判断你要不要启动 Web 服务器的逻辑完全取决于它从 classpath 里扫描到了什么依赖。这个报错波及的人群很广刚上手 Spring Boot 的新手、从 Spring Boot 1.x 升级到 2.x/3.x 的老手、用 IDEA 打开别人项目后直接点 Run 的同学都会遇到。有些人能很快解决有些人却折腾一两个小时问题往往出在一个很小但极易被忽略的细节上。2. 根本原理Spring Boot 是怎么决定“我要不要启动 Web 服务器”的要解决这个报错不能只停留在加依赖的层面得先把 Spring Boot 对 Web 应用类型的判断机制讲清楚。Spring Boot 在启动时会执行一个核心步骤——推断当前应用的WebApplicationType。这个类型一共有三种类型推断依据典型场景NONEclasspath 中没有任何 Web 相关依赖纯后台任务、定时任务SERVLETclasspath 中存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext传统 Servlet Web 应用绝大多数 Spring Boot Web 项目REACTIVEclasspath 中存在org.springframework.web.reactive.DispatcherHandler且不存在Servlet相关类WebFlux 响应式项目如果推断结果为 NONESpring Boot 会以非 Web 应用方式启动不会创建内嵌 Tomcat/Jetty/Undertow。这时候如果你在代码里写了RestController、Controller这类 Web 层注解或者调用了需要 Servlet 容器的组件应用能启动成功但永远不会监听端口。而missing ServletWebServerFactory这个报错实际上是 Spring Boot 在项目里已经存在 Web 相关代码比如你有 controller、有SpringBootApplication主类但它在上下文里找不到ServletWebServerFactory的实现类时主动拒绝继续启动。它给出的 Action 提示也很有指导性加spring-boot-starter-web到 classpath或者显式声明一个ServletWebServerFactory的Bean关键逻辑就藏在这里Spring Boot 自动配置里的ServletWebServerFactoryAutoConfiguration是条件装配的。它的生效条件是ConditionalOnClass(ServletRequest.class) ConditionalOnWebApplication(type Type.SERVLET)也就是说只有当 classpath 里有 Servlet API且应用被判定为 Servlet Web 应用时Spring Boot 才会自动配置 Tomcat 等内嵌容器相关的工厂 Bean。缺少任何一环这个自动配置类都不会生效。从 Spring Boot 2.3 开始官方还用spring-boot-web-server-*的方式简化了内嵌 Web 服务器的切换比如只引入spring-boot-starter-tomcat但最常用的还是完整引入spring-boot-starter-web它会帮你带上Spring MVC内嵌 TomcatJackson JSON 处理Spring Boot 对 Web 场景的全部自动配置所以当你看到这个报错时脑子里要有一个检查顺序classpath 里有没有 servlet-api → 有没有 spring-webmvc → 有没有内嵌容器实现 → Spring Boot 自动配置有没有被加载。绝大多数情况下崩溃点都出在第一环或最后一环。3. 逐个排查哪些原因会造成这个报错3.1 最常见的原因pom.xml 漏掉了 spring-boot-starter-web这个原因占所有报错场景的七成以上。尤其是当你用 IDEA 的 Spring Initializr 创建项目时没有勾选 Spring Web或者从网上找了一个代码片段只复制了 controller 层代码却没有复制完整的 pom.xml。打开你的 pom.xml注意检查这一块parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent如果 parent 在再看 dependencies 里是否有dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency两样都齐了再确认 parent 里声明的spring-boot-starter-parent的版本号是真实存在的。版本号写一个不存在的值依赖解析会失败classpath 会缺一堆东西报的错也五花八门。还有一个小概率场景有人喜欢用spring-boot-starter-webflux来做 Web 开发。WebFlux 是响应式栈而非 Servlet 栈。如果你同时引入了 webflux 和 web 两个 starterSpring Boot 会优先判定为 REACTIVE 类型此时 Tomcat 不会被配置同样可能出现 ServletWebServerFactory 相关的异常。解决方案是只保留一个 Web starter绝大多数业务项目选spring-boot-starter-web即可。3.2 常见原因之二Spring Boot 版本与依赖不兼容这个坑我踩过好几次属于那种配置看着全对但就是起不来的情况。Spring Boot 2.x 和 3.x 是两条完全不同的技术基线。Spring Boot 2.x 基于javax.servletAPISpring Boot 3.x 基于jakarta.servletAPI。如果你把spring-boot-starter-web的版本锁在了 2.x而项目其他组件引入了 Jakarta 命名空间下的 Servlet 相关类或者反过来自动配置的匹配条件就会失效。再细说一个更隐蔽的你不小心多加了一个javax.servlet-api的依赖但版本是 4.0.1而 Spring Boot 2.7 内部自带的是 Tomcat 9.0.x对应 Servlet 4.0。这本身没问题。但如果你手动加的是javax.servlet-api3.x内嵌 Tomcat 8 的某些初始化路径就会异常更糟糕的是可能导致ServletWebServerFactoryAutoConfiguration的ConditionalOnClass判断通过但真正的容器工厂创建失败。所以我的建议是除非你明确知道自己需要自定义 Servlet 容器版本否则不要在 Spring Boot 项目中手动引入任何 Servlet API 依赖把版本选择权完全交给 Spring Boot 的 BOMBill of Materials。BOM 已经替你统一管理了 Tomcat、Jetty、Undertow 的版本你手动加旧版本反而会打破这个平衡。3.3 常见原因之三自动配置被禁用Spring Boot 允许你在application.properties/application.yml里用一个开关关掉某些自动配置spring.autoconfigure.excludeorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration这个写法的场景是你确实不需要内嵌 Web 服务器想用外部 Tomcat 部署 war 包或者你想完全自己手动定义容器。但如果你不小心从网上复制了一段配置没注意或者因为排错时试过这个项而忘了删Spring Boot 就不会去创建 ServletWebServerFactory。这种情况的判断方法是去项目里搜一下spring.autoconfigure.exclude一眼就能看出来。有就删掉基本解决。同理还有一个原因是SpringBootApplication的exclude属性里手动排除了这个自动配置类SpringBootApplication(exclude { ServletWebServerFactoryAutoConfiguration.class })这种写法同样会导致报错但出现概率比 properties 里的更低因为它需要你精确写出类名误配的可能性不大。3.4 不常见但很坑IDEA 缓存和 Maven 依赖状态异常有一类场景是代码和配置都对但项目就是起不来。这时候十有八九是 IDE 缓存或 Maven 本地仓库出了问题。IDEA 对 Maven 依赖的解析有自己的缓存机制。如果你改动了 pom.xmlIDEA 没有重新导入或者导入过程半途失败classpath 就会处于一个看起来改了实际上没生效的中间态。常见的表现就是你明明加了spring-boot-starter-web重新点运行报错依旧。这种场景的排查步骤通常是先看 IDEA 右侧 Maven 面板展开Dependencies找一下有没有spring-boot-starter-web。如果没找到说明依赖导入没有完成。点 Maven 面板上方的刷新按钮Reload All Maven Projects。如果刷新后还是不行执行 Maven 的clean再接执行package看看命令行里是不是有依赖解析失败的报错。如果 IDE 无论如何都不正常直接放弃 IDEA 里的旧状态用终端命令验证mvn clean compile如果命令行里编译通过说明 Maven 本身没问题问题就在 IDE 的缓存索引。这时候可以尝试mvn -U clean install强制更新快照依赖并重新生成本地仓库缓存。再不行就重启 IDEA让它重新建立索引。Maven 本地仓库本身也可能存在损坏状态。.m2/repository里的_remote.repositories、.lastUpdated文件如果残留了一些失败状态会导致依赖解析时拿不到正确版本。最简单的暴力方案是删掉.m2/repository/org/springframework/boot整个目录重新让 Maven 下载。代价是又要等好几分钟的下载但至少排除一个变量。3.5 IDEA 社区版特有的一个问题热搜词里出现大量intellij idea 社区版相关的搜索这也说明很多人在用社区版跑 Spring Boot。IDEA 社区版必须明确一点它本身不内置对于 Spring Boot 项目的完整框架支持但它可以像普通 Java 项目一样编译运行 Spring Boot 应用。问题出在社区版对 Maven 项目的导入有时不会自动触发 Spring 插件相关的 facet 配置导致项目结构看起来有点卡。不过这不影响应用启动。真正容易在社区版上踩的坑是你用社区版直接打开了一个从 Gitee 上拉取的多模块 Maven 项目父模块依赖子模块但子模块没有正确安装到本地仓库Spring Boot 主模块在启动时找不到兄弟模块的类报错五花八门解决方式是先把所有模块 install 到本地仓库mvn clean install -DskipTests然后在主模块的 pom 里确认依赖的是项目版本号而不是 SNAPSHOT 缺省值。这个操作在专业版和社区版中并无差别主要是很多新人不知道要这么做。4. 实操解决步骤从报错到跑通的完整流程把排查思路串成一套可复制的步骤。这套流程我自己在带新人时反复用过基本能在十分钟内定位问题。4.1 第一步确认项目类型和启动方式先看项目是被当成什么方式启动的。在 IDEA 里点开右上角的 Run Configuration看看你是用Spring Boot模式启动还是用Application普通 Java 模式启动。Spring Boot 3 项目必须有主类而且主类上要有SpringBootApplication注解。确认你运行的类就是这个主类而不是某个Configuration类也不是某个测试类。package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }如果你在这个主类里加了spring.main.web-application-typenone配置或者在 application.yml 里写了spring.main.web-application-type: none那你等于强制告诉 Spring Boot 这不是 Web 应用。这种情况下的missing ServletWebServerFactory是无意义的——你手动掐断了 Web 启动路径。检查一下有没有这个配置。4.2 第二步classpath 完整性检查这是最快的排查动作。打开 pom.xml确认依赖里包含dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency如果你用的是 Gradle对应的是 build.gradleimplementation org.springframework.boot:spring-boot-starter-web确认无误后在 IDEA 右侧 Maven 面板里展开Dependencies搜索tomcat-embed-core、spring-webmvc、spring-boot-starter-tomcat这三个关键 artifact。如果任何一个不在列表里说明依赖并没有真正引入。这时候先执行mvn clean install再点 IDEA 的刷新按钮。4.3 第三步用“排除法”定位自动配置是否生效如果你确定依赖没问题但还是报错可以考虑在启动类上临时加一个调试输出确认 WebApplicationType 的推断结果SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication app new SpringApplication(DemoApplication.class); // 打印推断出的 Web 应用类型 System.out.println(Web type: app.getWebApplicationType()); app.run(args); } }这里以 Spring Boot 2.x 为例。getWebApplicationType方法会返回NONE、SERVLET或REACTIVE三种枚举值。如果输出的是NONE说明 classpath 里根本没有 Servlet 相关的类回到第二步如果输出是SERVLET但依然报错那问题大概率出在自动配置被禁用或容器工厂创建失败回到第三步继续查。4.4 第四步查自动配置排除项搜索整个项目包括 application.properties、application.yml、启动类注解确认没有以下任何一项spring.autoconfigure.excludeorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfigurationSpringBootApplication(exclude { ServletWebServerFactoryAutoConfiguration.class })如果搜到了直接删掉。除非你真的知道自己在做什么否则这个配置不该出现在普通 Web 项目里。4.5 第五步清理 IDEA 缓存和 Maven 仓库到了这一步还没解决就要动用杀招了先关闭 IDEA。删除项目里的.idea目录注意先备份最好不要直接在命令行里强势删除用 IDEA 的失效缓存功能更稳妥。重启 IDEA重新打开项目让它重新导入 Maven。如果依赖还是异常进入 IDEA 设置Settings → Build, Execution, Deployment → Build Tools → Maven → Local repository把路径记下来然后打开该目录找到org/springframework/boot目录将其改名为org/springframework/boot_backup强制重新下载。重新执行mvn clean install -DskipTests。这套操作下来90% 的莫名奇妙问题都会消失。剩下的 10% 里大多是系统 JDK 版本不匹配这个可以通过java -version和 pom.xml 里的java.version做对比排查。5. 常见问题与排查技巧实录以下这些问题都是我在实际交流中见过的真实案例整理成速查表方便你直接对照。问题现象可能原因快速验证办法刚创建项目就报 missing ServletWebServerFactory创建项目时没勾选 Spring Web检查 pom.xml 是否有 spring-boot-starter-web明明加了 starter 还报错IDEA 没有重新加载 Maven 依赖点 Maven 面板刷新按钮跑 mvn clean compile项目能启动但不监听端口spring.main.web-application-typenone检查 application.yml 里的配置加了自己下的 servlet-api 后报错Servlet API 版本和容器不匹配删掉手动引入的 servlet-api 依赖交给 BOM 管理从 Gitee 拉的项目报错指向自己的模块类多模块未先 install在父模块执行 mvn clean install主类能编译但启动报错JDK 版本和 Spring Boot 不匹配确认 Spring Boot 3.x 需要 JDK 17上次能跑这次突然报错IDEA 缓存状态损坏重启 IDEA删除 .idea 目录重新导入这里特别提醒一个容易被忽略的检查项IDEA 里的 Language Level 设置。如果你的 IDEA 里项目编译器默认设置成了 Java 8而 Spring Boot 3.x 必须要 Java 17IDE 的编译阶段可能不会报错但 Spring Boot 启动时的字节码版本校验就会失败报错信息经常莫名其妙不一定是 missing ServletWebServerFactory但也可能是这一条异常链上的间接后果。所以打开Settings → Build, Execution, Deployment → Compiler → Java Compiler把版本调成和你 pom 里java.version一致的版本。6. 还需要注意的同类报错变体在实际开发中missing ServletWebServerFactory有时不是孤立出现的。它可能是另外两个报错的前奏或者变体。第一个变体是No qualifying bean of type ServletWebServerFactory available出现这个报错说明ServletWebServerFactoryAutoConfiguration已经生效但容器工厂没有被成功创建。常见原因是你自定义了一个WebServerFactoryCustomizer但引用了不存在的类或者在配置类里写了某些 Tomcat 相关的初始化代码导致 Bean 创建失败。建议先移除所有自定义的WebServerFactoryCustomizer、TomcatConnectorCustomizer等配置再逐步加回来定位是哪个自定义逻辑破坏了容器创建。第二个变体是Port already in use: 8080这个虽然不是 missing ServletWebServerFactory但和它属于同一类容器启动异常。如果你的 8080 端口被占用Spring Boot 会宣布启动失败报错信息里也会有 ServletWebServerFactory 的身影。排查方式是用命令看看谁占用了端口netstat -ano | findstr 8080 taskkill /F /PID PID以上就是我个人在反复踩坑后总结出的全流程排查思路。遇到这个报错不用慌先想清楚 Spring Boot 推断 Web 应用类型的机制再按依赖、自动配置、IDE 状态三个方向去定位。尤其是当你对自己的配置很有信心时多想想是不是 IDE 缓存这个隐藏杀手在捣乱——我至少有两次花了半小时看代码最后靠重启 IDEA 解决。