本文副标题:解决 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。
最后,这个案例带给我的最大收获是通过错误堆栈信息以及源码可以更快的分析出错误的来源。