StackTraces

Spring Boot 啟動失敗:先定階段,再進分支

把啟動失敗拆成 6 個階段、12 條可對號入座的分支:先讀 Description 與 Action,再確認卡在哪個啟動階段,最後按報錯簽名展開分支,用判據式命令鎖定成因。大多數啟動失敗不需要改程式碼,問題出在環境或設定。

排查全景圖
全景導航 · 6 階段 × 12 條分支,先在圖上定位現象,再進下方路徑

按首個可操作的例外分流

先讀完整例外鏈與 Failure Analysis,記錄運行環境和生效設定,再按具體簽名選擇路徑。每條路徑統一為六段:當前環境、報錯識別、快速解法、驗證、解法無效時、相關。

12 條路徑 →
01 · 環境

當前環境

先記錄 Spring Boot 版本、構建 JDK、編譯工具鏈、部署鏡像、容器運行時和實際啟動命令。容器運行時必須在容器內核驗 java -version,不能用構建機或宿主機版本代替。

  • 確認報錯類來自應用產物還是第三方 JAR,並核對框架、插件和依賴的 Java 支持矩陣
  • 記錄編譯設定的 --release、toolchain、source/target,以及多階段構建最終鏡像實際帶入的 JRE/JDK
02 · 簽名

報錯識別

例外簽名 / SIGNATURE
java.lang.UnsupportedClassVersionError:
  com/acme/App has been compiled by a more recent version
  of the Java Runtime (class file version 61.0),
  this version only recognizes class file versions up to 55.0
常見變體 / VARIANTS
Error: A JNI error has occurred, please check your installation and try again
Unsupported major.minor version 61.0
java.lang.IllegalArgumentException: Unsupported class file major version 61
NoClassDefFoundError: Could not initialize class com.acme.App
  • UnsupportedClassVersionError → 運行期類加載失敗,提示裡會給出「需要的版本」和「當前上限」兩個數字
  • Error: A JNI error has occurred → 啟動器層的提示,緊跟其後的才是 UnsupportedClassVersionError
  • Unsupported major.minor version → 舊版 Java 的同義報錯,數字規則完全相同
  • Unsupported class file major version 61 → 構建或運行期的字節碼工具(ASM、ByteBuddy、CGLIB)讀不懂新版本
  • NoClassDefFoundError / ExceptionInInitializerError → 常是版本不兼容引發的次生錯誤,先回到最初的版本例外

class 文件版本對照:52.0=Java 8,55.0=Java 11,61.0=Java 17,65.0=Java 21。不要把 56.0 讀成「Java 56」。

03 · 安全操作

快速解法

升級運行時是首選;必須留在舊 JVM 時用 release 重新編譯。

升級運行時(首選)— 改基礎鏡像,光改構建機沒用

# Dockerfile
FROM eclipse-temurin:17-jre
COPY target/app.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

必須留在舊 JVM — 用 release 重新編譯

<properties>
  <java.version>11</java.version>
</properties>

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <release>11</release>
  </configuration>
</plugin>

報錯類來自第三方依賴 — 先確認是哪個 JAR

mvn dependency:tree -Dincludes=com.example:legacy-lib
04 · 確認

驗證

  1. 在最終部署鏡像或容器內執行 java -version,確認啟動腳本沒有調用另一套 JRE
  2. 檢查報錯類所在 JAR 的 class major 與運行 JVM 上限,確認構建產物和鏡像標籤已更新
  3. 重新啟動應用,確認不再出現 UnsupportedClassVersionError,並執行健康檢查
05 · 繼續診斷

解法無效時

  1. 容器內版本仍不符時,檢查多階段 Docker 構建的最終階段、基礎鏡像、PATH、JAVA_HOME
  2. class 版本仍過高時,用 jar tf 定位報錯類所屬 JAR,再檢查依賴樹
  3. 版本兼容後仍啟動失敗時,以新的最深層例外繼續分流
06 · 相鄰原因

相關

  • class file version 56.0 是 Java 12;61.0 是 Java 17,55.0 是 Java 11
  • Unsupported major.minor version 是較舊 Java 的類似版本錯誤;NoSuchMethodError 更偏向二進制 API 版本衝突
  • 構建機、宿主機、容器鏡像和容器運行時可能使用不同 Java;最終以真正執行啟動命令的 JVM 為準

常見問題

啟動報 UnsupportedClassVersionError(Unsupported major.minor version)怎麼解決?
編譯產物的字節碼版本高於運行期 JRE,容器裡常見於構建機用 JDK 17、鏡像卻帶 JRE 11。首選升級運行時基礎鏡像,並在容器內核驗 java -version;必須留在舊 JVM 時,用 maven-compiler-plugin 的 <release> 重新編譯,再用 dependency:tree 確認報錯類不是第三方 JAR 帶入的。
啟動報 Could not resolve placeholder,佔位符為什麼解析不出來?
Environment 裡找不到設定中引用的鍵,來源可能是 application.yml、環境變數或外部設定。先把例外裡的鍵名抄下來,逐處核對拼寫與真實來源,並確認當前激活的 Profile 與容器內的環境變數、掛載文件是否一致。
運行期拋 NoSuchMethodError 或 ClassNotFoundException,是依賴衝突嗎?
多為同一個庫在依賴樹裡存在多個版本,或 Spring Cloud 與 Spring Boot 版本隨意組合,運行時加載到了不兼容的類。用 dependency:tree 定位衝突並以 dependencyManagement 鎖定版本,按官方版本矩陣對齊組合。
啟動報 Port 8080 was already in use 怎麼處理?
端口已被其它進程佔用。二選一:用 lsof -i :8080 找到並停掉佔用進程,或在設定裡換一個端口。容器場景要分別核對容器內監聽端口、宿主機映射端口與同一網絡命名空間裡的進程,避免改錯了一層。
啟動報 Failed to configure a DataSource 是什麼原因?
引入了數據源相關的 starter,卻沒有可用的連接設定,自動設定裝配不出 DataSource。看最深層 Caused by 對號入座:補上 spring.datasource.url 等設定,或在確實不需要數據庫時排除 DataSourceAutoConfiguration。
配好了 spring.datasource.url 仍連不上庫,報 Access denied 或 Communications link failure?
設定已經存在,但連庫被拒或網絡不通。Access denied 多為賬號密碼或授權問題,Communications link failure 多為地址端口不可達。先把報錯文案對上,再分別核對主機、端口、庫名、憑據與網絡連通性。
啟動報 UnsatisfiedDependencyException,Bean 注入失敗怎麼辦?
某個 Bean 缺少它依賴的另一個 Bean,通常是目標類不在掃描根包內、條件裝配沒生效或模塊依賴缺失。報錯已經指明是哪個類、哪個字段,按這條線索核對元件掃描範圍、Profile 與條件註解,再對症修復。
報 A bean with that name has already been defined 怎麼解決?
兩個設定類或元件定義了同名 Bean。成因唯一,全局搜這個名字找到所有定義處,刪除重複定義或給其中一個改名,不要靠 Bean 覆蓋順序硬扛。
構造器注入報 BeanCurrentlyInCreationException,循環依賴怎麼破?
兩個 Bean 通過構造器互相依賴形成閉環,Spring 無法在構造階段提前暴露半成品。首選重構解耦,例如抽出公共依賴或調整職責;急著上線時才用 @Lazy 打破閉環。先確認是真的成環,而不是創建期間的二次引用。
啟動日誌中文亂碼怎麼修?
文件編碼與控制台編碼不一致,不是程式碼問題。把 JVM 與終端兩邊統一為 UTF-8,啟動時加 -Dfile.encoding=UTF-8,並確認終端與容器 locale 同樣支持 UTF-8。
啟動就報 OutOfMemoryError: Java heap space?
啟動期 OOM 基本都是堆上限太小,而不是記憶體洩漏。先調大 -Xmx 後重試;調大仍失敗,再看是否有大物件在啟動期一次性加載,才考慮深挖洩漏。
報 Unable to start embedded Tomcat,端口和記憶體都排除了?
排除端口占用與堆記憶體後,剩下的多與系統資源有關,例如執行緒數、文件句柄上限。先看完整的 Caused by 鏈確認具體資源類型,再調大對應的系統資源上限後重試。