本文副標題:解決 NoSuchMethodError 異常
java.lang.NoSuchMethodError: org.slf4j.MDC.getCopyOfContextMap()Ljava/util/Map
-------------------------------------------------------
今天在一臺機器上配置完 spring-activemq 後,可以無障礙的運行測試代碼。但是,完全相同的代碼提交後在另一臺機器死活跑不通。主要的錯誤堆棧信息如下
Caused by: java.lang.NoSuchMethodError: org.slf4j.MDC.getCopyOfContextMap()Ljava/util/Map;
at org.apache.activemq.util.MDCHelper.getCopyOfContextMap(MDCHelper.java:30)
at org.apache.activemq.thread.PooledTaskRunner.<init>(PooledTaskRunner.java:42)
Java.lang.NoSuchMethodError 的 javadoc 註釋如下:
/**
* Thrown if an application tries to call a specified method of a
* class (either static or instance), and that class no longer has a
* definition of that method.
* <p>
* Normally, this error is caught by the compiler; this error can
* only occur at run time if the definition of a class has
* incompatibly changed.
*
* @author unascribed
* @version %I%, %G%
* @since JDK1.0
*/
通過註釋( this error can only occur at run time if the definition of a class has incompatibly changed. )可以初步推斷是因爲版本不兼容導致的。
NOTE: 錯誤信息裏方法名後的 Ljava/util/Map 代表的是該方法的返回值類型。
再結合所缺失方法(org.slf4j.MDC.getCopyOfContextMap)的 Javadoc:
/**
* Return a copy of the current thread's context map, with keys and values of
* type String. Returned value may be null.
*
* @return A copy of the current thread's context map. May be null.
* @since 1.5.1
*/
public static Map getCopyOfContextMap() {
從 @since 1.5.1 可以看出,該方法需要高於1.5.1 的 slf4j 版本。
在工程 pom 下查看所依賴的 slf4j 版本發現所使用的是 1.5.11,從版本看是沒有問題的。那麼錯誤究竟從哪兒來的?從同事那裏知道之前碰到過這個問題,當時解決的方法是將 JBoss 的版本從5.0.0換到了5.1.0。試了一下,用這個辦法確實可以解決問題。但是,我並不滿足於只是解決了問題,如果只是這樣,那麼前面的分析都白費了。
我想驗證的是:Jboss 5.0.0 依賴的 sfl4j 版本是1.5.1之前的,而jboss 5.1.0 依賴的是slf4j 1.5.1之後的某個版本。經過一翻對比,確實證實了這個猜測。以下來自 jboss 根目錄下的 jar-versions.xml :
jboss 5.0.0 用的 slf4j 是 1.5.0
<jar name="slf4j-api.jar" specVersion="5.0.0.GA" specVendor="JBoss (http://www.jboss.org/)" specTitle="JBoss" implVersion="1.5.0" implVendor="JBoss Inc." implTitle="slf4j-api" implVendorID="http://www.jboss.org/" implURL="http://www.jboss.org/" sealed="false" md5Digest="d0841573796aa6b6dcf311e7e8f07a95"/>
jboss 5.1.0 用的 slf4j 是 1.5.6
<jar name="slf4j-api.jar" specVersion="5.1.0.GA" specVendor="JBoss (http://www.jboss.org/)" specTitle="JBoss" implVersion="1.5.6" implVendor="JBoss Inc." implTitle="slf4j-api" implVendorID="http://www.jboss.org/" implURL="http://www.jboss.org/" sealed="false" md5Digest="63720afbf77db737e305c85388302650"/>
通過這次的分析也可以看出,工程中依賴的 jar 包如果跟 jboss 自身依賴的 jar 版本衝突,會優先使用 jboss 的 jar。
最後,這個案例帶給我的最大收穫是通過錯誤堆棧信息以及源碼可以更快的分析出錯誤的來源。