
1. 問題現(xiàn)象與影響范圍先描述一下這個報錯的樣子。如果你在 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 和端口號應(yīng)用就退出。這種情況在 Spring Boot 2.x 項目中非常典型但不少人在第一次遇到時會被ServletWebServerFactory這個類名嚇住以為是自己寫的某個配置寫錯了或者 IDEA 的 Run Configuration 配錯了。實(shí)際上這個錯誤的核心含義是Spring Boot 沒有在應(yīng)用上下文里找到一個可用于創(chuàng)建內(nèi)嵌 Web 服務(wù)器的工廠 Bean。換句話說Spring Boot 根本不知道你要跑的是一個 Web 應(yīng)用。而 Spring Boot 判斷你要不要啟動 Web 服務(wù)器的邏輯完全取決于它從 classpath 里掃描到了什么依賴。這個報錯波及的人群很廣剛上手 Spring Boot 的新手、從 Spring Boot 1.x 升級到 2.x/3.x 的老手、用 IDEA 打開別人項目后直接點(diǎn) Run 的同學(xué)都會遇到。有些人能很快解決有些人卻折騰一兩個小時問題往往出在一個很小但極易被忽略的細(xì)節(jié)上。2. 根本原理Spring Boot 是怎么決定“我要不要啟動 Web 服務(wù)器”的要解決這個報錯不能只停留在加依賴的層面得先把 Spring Boot 對 Web 應(yīng)用類型的判斷機(jī)制講清楚。Spring Boot 在啟動時會執(zhí)行一個核心步驟——推斷當(dāng)前應(yīng)用的WebApplicationType。這個類型一共有三種類型推斷依據(jù)典型場景NONEclasspath 中沒有任何 Web 相關(guān)依賴純后臺任務(wù)、定時任務(wù)SERVLETclasspath 中存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext傳統(tǒng) Servlet Web 應(yīng)用絕大多數(shù) Spring Boot Web 項目REACTIVEclasspath 中存在org.springframework.web.reactive.DispatcherHandler且不存在Servlet相關(guān)類WebFlux 響應(yīng)式項目如果推斷結(jié)果為 NONESpring Boot 會以非 Web 應(yīng)用方式啟動不會創(chuàng)建內(nèi)嵌 Tomcat/Jetty/Undertow。這時候如果你在代碼里寫了RestController、Controller這類 Web 層注解或者調(diào)用了需要 Servlet 容器的組件應(yīng)用能啟動成功但永遠(yuǎn)不會監(jiān)聽端口。而missing ServletWebServerFactory這個報錯實(shí)際上是 Spring Boot 在項目里已經(jīng)存在 Web 相關(guān)代碼比如你有 controller、有SpringBootApplication主類但它在上下文里找不到ServletWebServerFactory的實(shí)現(xiàn)類時主動拒絕繼續(xù)啟動。它給出的 Action 提示也很有指導(dǎo)性加spring-boot-starter-web到 classpath或者顯式聲明一個ServletWebServerFactory的Bean關(guān)鍵邏輯就藏在這里Spring Boot 自動配置里的ServletWebServerFactoryAutoConfiguration是條件裝配的。它的生效條件是ConditionalOnClass(ServletRequest.class) ConditionalOnWebApplication(type Type.SERVLET)也就是說只有當(dāng) classpath 里有 Servlet API且應(yīng)用被判定為 Servlet Web 應(yīng)用時Spring Boot 才會自動配置 Tomcat 等內(nèi)嵌容器相關(guān)的工廠 Bean。缺少任何一環(huán)這個自動配置類都不會生效。從 Spring Boot 2.3 開始官方還用spring-boot-web-server-*的方式簡化了內(nèi)嵌 Web 服務(wù)器的切換比如只引入spring-boot-starter-tomcat但最常用的還是完整引入spring-boot-starter-web它會幫你帶上Spring MVC內(nèi)嵌 TomcatJackson JSON 處理Spring Boot 對 Web 場景的全部自動配置所以當(dāng)你看到這個報錯時腦子里要有一個檢查順序classpath 里有沒有 servlet-api → 有沒有 spring-webmvc → 有沒有內(nèi)嵌容器實(shí)現(xiàn) → Spring Boot 自動配置有沒有被加載。絕大多數(shù)情況下崩潰點(diǎn)都出在第一環(huán)或最后一環(huán)。3. 逐個排查哪些原因會造成這個報錯3.1 最常見的原因pom.xml 漏掉了 spring-boot-starter-web這個原因占所有報錯場景的七成以上。尤其是當(dāng)你用 IDEA 的 Spring Initializr 創(chuàng)建項目時沒有勾選 Spring Web或者從網(wǎng)上找了一個代碼片段只復(fù)制了 controller 層代碼卻沒有復(fù)制完整的 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兩樣都齊了再確認(rèn) parent 里聲明的spring-boot-starter-parent的版本號是真實(shí)存在的。版本號寫一個不存在的值依賴解析會失敗classpath 會缺一堆東西報的錯也五花八門。還有一個小概率場景有人喜歡用spring-boot-starter-webflux來做 Web 開發(fā)。WebFlux 是響應(yīng)式棧而非 Servlet 棧。如果你同時引入了 webflux 和 web 兩個 starterSpring Boot 會優(yōu)先判定為 REACTIVE 類型此時 Tomcat 不會被配置同樣可能出現(xiàn) ServletWebServerFactory 相關(guān)的異常。解決方案是只保留一個 Web starter絕大多數(shù)業(yè)務(wù)項目選spring-boot-starter-web即可。3.2 常見原因之二Spring Boot 版本與依賴不兼容這個坑我踩過好幾次屬于那種配置看著全對但就是起不來的情況。Spring Boot 2.x 和 3.x 是兩條完全不同的技術(shù)基線。Spring Boot 2.x 基于javax.servletAPISpring Boot 3.x 基于jakarta.servletAPI。如果你把spring-boot-starter-web的版本鎖在了 2.x而項目其他組件引入了 Jakarta 命名空間下的 Servlet 相關(guān)類或者反過來自動配置的匹配條件就會失效。再細(xì)說一個更隱蔽的你不小心多加了一個javax.servlet-api的依賴但版本是 4.0.1而 Spring Boot 2.7 內(nèi)部自帶的是 Tomcat 9.0.x對應(yīng) Servlet 4.0。這本身沒問題。但如果你手動加的是javax.servlet-api3.x內(nèi)嵌 Tomcat 8 的某些初始化路徑就會異常更糟糕的是可能導(dǎo)致ServletWebServerFactoryAutoConfiguration的ConditionalOnClass判斷通過但真正的容器工廠創(chuàng)建失敗。所以我的建議是除非你明確知道自己需要自定義 Servlet 容器版本否則不要在 Spring Boot 項目中手動引入任何 Servlet API 依賴把版本選擇權(quán)完全交給 Spring Boot 的 BOMBill of Materials。BOM 已經(jīng)替你統(tǒng)一管理了 Tomcat、Jetty、Undertow 的版本你手動加舊版本反而會打破這個平衡。3.3 常見原因之三自動配置被禁用Spring Boot 允許你在application.properties/application.yml里用一個開關(guān)關(guān)掉某些自動配置spring.autoconfigure.excludeorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration這個寫法的場景是你確實(shí)不需要內(nèi)嵌 Web 服務(wù)器想用外部 Tomcat 部署 war 包或者你想完全自己手動定義容器。但如果你不小心從網(wǎng)上復(fù)制了一段配置沒注意或者因為排錯時試過這個項而忘了刪Spring Boot 就不會去創(chuàng)建 ServletWebServerFactory。這種情況的判斷方法是去項目里搜一下spring.autoconfigure.exclude一眼就能看出來。有就刪掉基本解決。同理還有一個原因是SpringBootApplication的exclude屬性里手動排除了這個自動配置類SpringBootApplication(exclude { ServletWebServerFactoryAutoConfiguration.class })這種寫法同樣會導(dǎo)致報錯但出現(xiàn)概率比 properties 里的更低因為它需要你精確寫出類名誤配的可能性不大。3.4 不常見但很坑IDEA 緩存和 Maven 依賴狀態(tài)異常有一類場景是代碼和配置都對但項目就是起不來。這時候十有八九是 IDE 緩存或 Maven 本地倉庫出了問題。IDEA 對 Maven 依賴的解析有自己的緩存機(jī)制。如果你改動了 pom.xmlIDEA 沒有重新導(dǎo)入或者導(dǎo)入過程半途失敗classpath 就會處于一個看起來改了實(shí)際上沒生效的中間態(tài)。常見的表現(xiàn)就是你明明加了spring-boot-starter-web重新點(diǎn)運(yùn)行報錯依舊。這種場景的排查步驟通常是先看 IDEA 右側(cè) Maven 面板展開Dependencies找一下有沒有spring-boot-starter-web。如果沒找到說明依賴導(dǎo)入沒有完成。點(diǎn) Maven 面板上方的刷新按鈕Reload All Maven Projects。如果刷新后還是不行執(zhí)行 Maven 的clean再接執(zhí)行package看看命令行里是不是有依賴解析失敗的報錯。如果 IDE 無論如何都不正常直接放棄 IDEA 里的舊狀態(tài)用終端命令驗證mvn clean compile如果命令行里編譯通過說明 Maven 本身沒問題問題就在 IDE 的緩存索引。這時候可以嘗試mvn -U clean install強(qiáng)制更新快照依賴并重新生成本地倉庫緩存。再不行就重啟 IDEA讓它重新建立索引。Maven 本地倉庫本身也可能存在損壞狀態(tài)。.m2/repository里的_remote.repositories、.lastUpdated文件如果殘留了一些失敗狀態(tài)會導(dǎo)致依賴解析時拿不到正確版本。最簡單的暴力方案是刪掉.m2/repository/org/springframework/boot整個目錄重新讓 Maven 下載。代價是又要等好幾分鐘的下載但至少排除一個變量。3.5 IDEA 社區(qū)版特有的一個問題熱搜詞里出現(xiàn)大量intellij idea 社區(qū)版相關(guān)的搜索這也說明很多人在用社區(qū)版跑 Spring Boot。IDEA 社區(qū)版必須明確一點(diǎn)它本身不內(nèi)置對于 Spring Boot 項目的完整框架支持但它可以像普通 Java 項目一樣編譯運(yùn)行 Spring Boot 應(yīng)用。問題出在社區(qū)版對 Maven 項目的導(dǎo)入有時不會自動觸發(fā) Spring 插件相關(guān)的 facet 配置導(dǎo)致項目結(jié)構(gòu)看起來有點(diǎn)卡。不過這不影響應(yīng)用啟動。真正容易在社區(qū)版上踩的坑是你用社區(qū)版直接打開了一個從 Gitee 上拉取的多模塊 Maven 項目父模塊依賴子模塊但子模塊沒有正確安裝到本地倉庫Spring Boot 主模塊在啟動時找不到兄弟模塊的類報錯五花八門解決方式是先把所有模塊 install 到本地倉庫mvn clean install -DskipTests然后在主模塊的 pom 里確認(rèn)依賴的是項目版本號而不是 SNAPSHOT 缺省值。這個操作在專業(yè)版和社區(qū)版中并無差別主要是很多新人不知道要這么做。4. 實(shí)操解決步驟從報錯到跑通的完整流程把排查思路串成一套可復(fù)制的步驟。這套流程我自己在帶新人時反復(fù)用過基本能在十分鐘內(nèi)定位問題。4.1 第一步確認(rèn)項目類型和啟動方式先看項目是被當(dāng)成什么方式啟動的。在 IDEA 里點(diǎn)開右上角的 Run Configuration看看你是用Spring Boot模式啟動還是用Application普通 Java 模式啟動。Spring Boot 3 項目必須有主類而且主類上要有SpringBootApplication注解。確認(rèn)你運(yùn)行的類就是這個主類而不是某個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那你等于強(qiáng)制告訴 Spring Boot 這不是 Web 應(yīng)用。這種情況下的missing ServletWebServerFactory是無意義的——你手動掐斷了 Web 啟動路徑。檢查一下有沒有這個配置。4.2 第二步classpath 完整性檢查這是最快的排查動作。打開 pom.xml確認(rèn)依賴?yán)锇琩ependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency如果你用的是 Gradle對應(yīng)的是 build.gradleimplementation org.springframework.boot:spring-boot-starter-web確認(rèn)無誤后在 IDEA 右側(cè) Maven 面板里展開Dependencies搜索tomcat-embed-core、spring-webmvc、spring-boot-starter-tomcat這三個關(guān)鍵 artifact。如果任何一個不在列表里說明依賴并沒有真正引入。這時候先執(zhí)行mvn clean install再點(diǎn) IDEA 的刷新按鈕。4.3 第三步用“排除法”定位自動配置是否生效如果你確定依賴沒問題但還是報錯可以考慮在啟動類上臨時加一個調(diào)試輸出確認(rèn) WebApplicationType 的推斷結(jié)果SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication app new SpringApplication(DemoApplication.class); // 打印推斷出的 Web 應(yīng)用類型 System.out.println(Web type: app.getWebApplicationType()); app.run(args); } }這里以 Spring Boot 2.x 為例。getWebApplicationType方法會返回NONE、SERVLET或REACTIVE三種枚舉值。如果輸出的是NONE說明 classpath 里根本沒有 Servlet 相關(guān)的類回到第二步如果輸出是SERVLET但依然報錯那問題大概率出在自動配置被禁用或容器工廠創(chuàng)建失敗回到第三步繼續(xù)查。4.4 第四步查自動配置排除項搜索整個項目包括 application.properties、application.yml、啟動類注解確認(rèn)沒有以下任何一項spring.autoconfigure.excludeorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfigurationSpringBootApplication(exclude { ServletWebServerFactoryAutoConfiguration.class })如果搜到了直接刪掉。除非你真的知道自己在做什么否則這個配置不該出現(xiàn)在普通 Web 項目里。4.5 第五步清理 IDEA 緩存和 Maven 倉庫到了這一步還沒解決就要動用殺招了先關(guān)閉 IDEA。刪除項目里的.idea目錄注意先備份最好不要直接在命令行里強(qiáng)勢刪除用 IDEA 的失效緩存功能更穩(wěn)妥。重啟 IDEA重新打開項目讓它重新導(dǎo)入 Maven。如果依賴還是異常進(jìn)入 IDEA 設(shè)置Settings → Build, Execution, Deployment → Build Tools → Maven → Local repository把路徑記下來然后打開該目錄找到org/springframework/boot目錄將其改名為org/springframework/boot_backup強(qiáng)制重新下載。重新執(zhí)行mvn clean install -DskipTests。這套操作下來90% 的莫名奇妙問題都會消失。剩下的 10% 里大多是系統(tǒng) JDK 版本不匹配這個可以通過java -version和 pom.xml 里的java.version做對比排查。5. 常見問題與排查技巧實(shí)錄以下這些問題都是我在實(shí)際交流中見過的真實(shí)案例整理成速查表方便你直接對照。問題現(xiàn)象可能原因快速驗證辦法剛創(chuàng)建項目就報 missing ServletWebServerFactory創(chuàng)建項目時沒勾選 Spring Web檢查 pom.xml 是否有 spring-boot-starter-web明明加了 starter 還報錯IDEA 沒有重新加載 Maven 依賴點(diǎn) Maven 面板刷新按鈕跑 mvn clean compile項目能啟動但不監(jiān)聽端口spring.main.web-application-typenone檢查 application.yml 里的配置加了自己下的 servlet-api 后報錯Servlet API 版本和容器不匹配刪掉手動引入的 servlet-api 依賴交給 BOM 管理從 Gitee 拉的項目報錯指向自己的模塊類多模塊未先 install在父模塊執(zhí)行 mvn clean install主類能編譯但啟動報錯JDK 版本和 Spring Boot 不匹配確認(rèn) Spring Boot 3.x 需要 JDK 17上次能跑這次突然報錯IDEA 緩存狀態(tài)損壞重啟 IDEA刪除 .idea 目錄重新導(dǎo)入這里特別提醒一個容易被忽略的檢查項IDEA 里的 Language Level 設(shè)置。如果你的 IDEA 里項目編譯器默認(rèn)設(shè)置成了 Java 8而 Spring Boot 3.x 必須要 Java 17IDE 的編譯階段可能不會報錯但 Spring Boot 啟動時的字節(jié)碼版本校驗就會失敗報錯信息經(jīng)常莫名其妙不一定是 missing ServletWebServerFactory但也可能是這一條異常鏈上的間接后果。所以打開Settings → Build, Execution, Deployment → Compiler → Java Compiler把版本調(diào)成和你 pom 里java.version一致的版本。6. 還需要注意的同類報錯變體在實(shí)際開發(fā)中missing ServletWebServerFactory有時不是孤立出現(xiàn)的。它可能是另外兩個報錯的前奏或者變體。第一個變體是No qualifying bean of type ServletWebServerFactory available出現(xiàn)這個報錯說明ServletWebServerFactoryAutoConfiguration已經(jīng)生效但容器工廠沒有被成功創(chuàng)建。常見原因是你自定義了一個WebServerFactoryCustomizer但引用了不存在的類或者在配置類里寫了某些 Tomcat 相關(guān)的初始化代碼導(dǎo)致 Bean 創(chuàng)建失敗。建議先移除所有自定義的WebServerFactoryCustomizer、TomcatConnectorCustomizer等配置再逐步加回來定位是哪個自定義邏輯破壞了容器創(chuàng)建。第二個變體是Port already in use: 8080這個雖然不是 missing ServletWebServerFactory但和它屬于同一類容器啟動異常。如果你的 8080 端口被占用Spring Boot 會宣布啟動失敗報錯信息里也會有 ServletWebServerFactory 的身影。排查方式是用命令看看誰占用了端口netstat -ano | findstr 8080 taskkill /F /PID PID以上就是我個人在反復(fù)踩坑后總結(jié)出的全流程排查思路。遇到這個報錯不用慌先想清楚 Spring Boot 推斷 Web 應(yīng)用類型的機(jī)制再按依賴、自動配置、IDE 狀態(tài)三個方向去定位。尤其是當(dāng)你對自己的配置很有信心時多想想是不是 IDE 緩存這個隱藏殺手在搗亂——我至少有兩次花了半小時看代碼最后靠重啟 IDEA 解決。