這個例外的本質是:Spring Boot 判斷應用是 Web 應用,決定啟動
ServletWebServerApplicationContext,但容器裡沒有一個能生成 Web 服務器的ServletWebServerFactorybean。要麼少了spring-boot-starter-web,要麼主類缺了@SpringBootApplication,要麼它本就不該當 Web 應用跑。
Issue & Env(案發現場與環境)
- 現象:應用啟動到一半直接失敗,控制台報 ApplicationContextException,進程退出。
- 環境:Spring Boot 2.x / 3.x。常見於寫批處理、CLI 工具卻引進了 Web 依賴,或單元測試沒開啟自動設定。
StackTrace(堆疊日誌)
標準啟動失敗堆疊:
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:163)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:596)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:305)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1307)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1296)
at com.example.demo.MainEntryPoint.main(MainEntryPoint.java:12)
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:209)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:179)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:158)
... 9 more
Reproduce(復現程式碼)
主類漏寫 @SpringBootApplication,SpringApplication.run 找不到自動設定入口:
public class MainEntryPoint {
public static void main(String[] args) {
SpringApplication.run(MainEntryPoint.class, args);
}
}
運行後就會在啟動階段拋上面那段例外。
Root Cause(根因分析)
SpringApplication 啟動時,會先根據 classpath 推斷應用類型:有 servlet 相關類就當作 SERVLET,有響應式就當作 REACTIVE,都沒有就是 NONE。一旦推斷成 SERVLET,它會創建 ServletWebServerApplicationContext。這個上下文要啟動內嵌容器,必須先從容器裡拿到一個 ServletWebServerFactory bean。
正常 Web 應用裡,@SpringBootApplication 的自動設定會根據 spring-boot-starter-web 註冊一個 TomcatServletWebServerFactory。三條路都會讓這個 bean 缺失:
- 主類缺
@SpringBootApplication,自動設定根本沒啟動。 - classpath 有 servlet API 或
spring-webmvc,卻沒帶spring-boot-starter-web。典型是批處理、CLI 項目被動引進了 Web 相關依賴,被誤判成 Web 應用。 - 測試裡用了
@SpringBootTest或@ContextConfiguration,但只加載了局部設定類,沒觸發自動設定。
The Fix(解決方案)
Web 應用就該有 Web 依賴和主類註解。檢查兩件事:主類補上 @SpringBootApplication;pom.xml 裡確認有 spring-boot-starter-web。
非 Web 應用(批處理、定時任務、CLI),明確告訴 Spring Boot 別啟動 Web 服務器:
spring.main.web-application-type=none
或 YAML 寫法:
spring:
main:
web-application-type: none
測試場景,給設定類或測試類補上 @EnableAutoConfiguration,或手動提供一個 ServletWebServerFactory bean。最省事的是讓 @SpringBootTest 走完整自動設定,而不是隻拼少數幾個設定類。
