在 Java 開發中,java.lang.IncompatibleClassChangeError: Implementing class 是一個讓人頭痛的錯誤,通常發生在類結構發生不兼容變更時。本文記錄一次真實的排查過程,希望能為遇到相同問題的開發者提供參考思路。
專案在 Git 更新代碼並重啟服務器後,突然拋出此異常。錯誤堆疊信息晦澀難懂,初步百度搜索多指向「JAR 包衝突」,於是懷疑是 Maven 依賴管理問題。
按慣例執行 clean -> package,甚至刪除本地倉庫、重新 Clone 專案,但問題依舊。為了隔離環境因素,脫離 IDEA 直接在命令列執行:
mvn install -Dmaven.test.skip=true -Dmaven.development=true
打包成功,且生成的 WAR 包在別人電腦的 Tomcat 上運行正常。這證明:Maven 本身無問題、IDEA 整合 Maven 正常、WAR 包本身無缺陷。
既然 WAR 包沒問題,矛頭指向 Tomcat。在乾淨的 Tomcat 環境下部署該 WAR 包,錯誤復現。關鍵日誌如下:
十二月 17, 2016 10:13:31 上午 org.apache.catalina.loader.WebappClassLoader validateJarFile
信息: validateJarFile(F:\eclipseworkspace\pop-auction-center-law_new\pop-auction-center-web\target\pop-auction-center-web\WEB-INF\lib\servlet-api-2.5.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class
十二月 17, 2016 10:13:46 上午 org.apache.tomcat.util.modeler.BaseModelMBean invoke
嚴重: Exception invoking method manageApp
java.lang.IncompatibleClassChangeError: Implementing class
at java.lang.ClassLoader.defineClass1(Native Method)
...
at org.apache.catalina.startup.ContextConfig.checkHandlesTypes(ContextConfig.java:1945)
...
日誌明確提示:WEB-INF/lib 下存在 servlet-api-2.5.jar,且其中包含 javax.servlet.Servlet 類,違反 Servlet 規範 9.7.2 節(容器已提供 Servlet API,Web 應用不得自行打包)。
根因: 專案的 pom.xml 將 servlet-api 以 compile 作用域引入,導致其被打包進 WAR 的 WEB-INF/lib,與 Tomcat 自帶的 servlet-api.jar 產生類加載衝突,引發 IncompatibleClassChangeError。
解決: 將 servlet-api 依賴作用域改為 provided:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
重新打包部署後,問題徹底解決。
Offending class)。重新安裝 Tomcat 只是治標,修正依賴作用域才是治本。這次經歷再次印證:分析問題、解決問題的方法論,比單純記憶錯誤方案更有價值。