經典的 **Jar 包地獄** 案例:Tomcat 7 啟動拋出 `java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()Ljavax/servlet/SessionCookieConfig`。本文從**異常本質、版本對應關係、三種解決方案、避坑原則**四個維度徹底解決此類版本衝突問題。
java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()Ljavax/servlet/SessionCookieConfig
衝突根因: 應用 `WEB-INF/lib` 下攜帶了**低版本 Servlet API Jar (如 2.5 版)**,類加載器**優先加載 WebApp 類加載器**中的類 (雙親委派模型被打破),導致運行時看到的是舊版 `ServletContext` 接口,**缺少 3.0 新增方法**。
| Servlet 版本 | Java EE 版本 | 關鍵特性 | Tomcat 版本 | Maven 坐標 |
|---|---|---|---|---|
| 2.5 | Java EE 5 | 註解支援、web.xml 可選 | Tomcat 6 | `javax.servlet:servlet-api:2.5` |
| 3.0 | Java EE 6 | 異步 Servlet、動態註冊、SessionCookieConfig | Tomcat 7 | `javax.servlet:javax.servlet-api:3.0.1` |
| 3.1 | Java EE 7 | 非阻塞 IO、协议升級 | Tomcat 8 | `javax.servlet:javax.servlet-api:3.1.0` |
| 4.0 | Java EE 8 | HTTP/2、Server Push | Tomcat 9 | `javax.servlet:javax.servlet-api:4.0.1` |
| 5.0 / 6.0 | Jakarta EE 9/10 | 包名遷移 `jakarta.servlet.*` | Tomcat 10/10.1 | `jakarta.servlet:jakarta.servlet-api:5.0.0/6.0.0` |
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version> <!-- 與 Tomcat 7 匹配 -->
<scope>provided</scope> <!-- 關鍵:編譯用,打包不進 WEB-INF/lib -->
</dependency>
原理: `provided` 範圍表示**「由容器運行時提供」**,Maven 打包 WAR 時**不會將該 Jar 放入 `WEB-INF/lib`**,運行時直接使用 Tomcat `lib` 目錄下的版本,**徹底消除衝突**。
<dependency>
<groupId>some.third.party</groupId>
<artifactId>some-lib</artifactId>
<version>x.y.z</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
適用於**無法控制第三方庫傳遞依賴**的情況,配合方案一使用。
原文方案: 把 tomcat 目錄下的 lib/servlet-api.jar 文件拷貝到 $JAVA_HOME/jre/lib/ext 目錄下,再啟動 tomcat 問題解決
缺陷:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
NoSuchMethodError = 編譯版本 ≠ 運行版本。
Servlet API **必須設為 provided 範圍**,讓容器統一提供運行時實現。
Tomcat 7 對應 Servlet 3.0,Maven 坐標 `javax.servlet:javax.servlet-api:3.0.1`。
**拒絕手動複製 Jar 到 JRE ext 目錄**,擁抱依賴管理工具,才是專業態度。