NoSuchMethodError通用解决方案

NoSuchMethodError 这种错误可能由两个原因导致。

1、项目运行时加载了错误jar包下的class

2、同一个类型的jar包的不同版本同时存在classpath中。

一、加载了错误jar包下的class

参考下面这个例子:

java.lang.NoSuchMethodError: org.apache.xmlbeans.XmlOptions.setEntityExpansionLimit(I)Lorg/apache/xmlbeans/XmlOptions;
        at org.apache.poi.ooxml.POIXMLTypeLoader.(POIXMLTypeLoader.java:43)
        at org.apache.poi.xssf.model.ThemesTable.(ThemesTable.java:86)

错误发生的背景:

POI 3.10 升级到 4.0.1,而POI会使用到xmlbeans.jar这个包。前者(v3.10)使用xmlbeans-2.3.0.jar,后者(v4.0.1)使用xmlbeans-3.0.2.jar。升级时,也确定xmlbeans 3.0.2.jar在classpath中,如下:

[INFO] |  |  \- org.apache.xmlbeans:xmlbeans:jar:3.0.2:compile

 查看XmlOptions.class的源码,也确实有setEntityExpansionLimit()方法。

NoSuchMethodError通用解决方案_第1张图片

分析思路:

觉得很不可思议,所以在项目代码中加入如下代码,来查看XmlOption.class是从哪个jar包中加载的,示例代码如下(作为参考)

ClassLoader classloader = XmlOptions.class.getClassLoader();
URL res = classloader.getResource("org/apache/xmlbeans/XmlOptions.class");
String path = res.getPath();
LOGGER.debug("XmlOptions.class loaded from " + path)

运行项目 ,相关log如下:

XmlOptions.class loaded from file:/D:/xxx/Weblogic12/weblogic_home/wlserver/modules/com.bea.core.xml.xmlbeans.jar!/org/apache/xmlbeans/XmlOptions.class

发现XmlOption.class是从weblogic的module目录下的com.bea.core.xml.xmlbeans.jar中加载的,使用java decompiler 工具查看该jar包下的XmlOption.class,确实没有setEntityExpansionLimit()这个方法。

那么,为什么项目发布到服务器后,会从weblogic中加载,而不是直接从项目classpath中加载呢?

默认情况下,所有的 class都会优先从服务器和jdk中加载,其次是项目classpath。如果需要修改class的加载优先级,让某些class优先从项目classpath中加载,需要在weblogic.xml的 标签中进行相关配置(不再赘述)。

解决方案:

在weblogic.xml的 标签中指定相关jar的加载优先级。

org.apache.xmlbeans.*

 二、同一个类型的jar包的不同版本同时存在classpath中。

参考如下例子:

java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.encodeBase64String([B)Ljava/lang/String;

分析:

Base64.class所在的jar包commons-codec-1.5.jar确定在classpath中,而且该jar下面的Base64.class也有相应的方法。

使用相同的方法:在项目代码中引入如下代码:

ClassLoader classloader = Base64.class.getClassLoader();
URL res = classloader.getResource("org/apache/commons/codec/binary/Base64.class");
String path = res.getPath();
LOGGER.debug("Base64.class loaded from " + path)

 重新编译项目,发布运行,查看log:

Base64.class loaded from file:/D:/xxx/Weblogic12/weblogic_home/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_user/projectname/89k7t8/war/WEB-INF/lib/commons-codec-1.2.jar

Base64.class从commons-codec-1.2.jar加载的,不是从commons-codec-1.5.jar加载的。

而log中所指向的目录正好是项目发到服务器时,项目的所有jar指向的目录。该目录下同时存在commons-codec-1.2.jar和commons-codec-1.5.jar(commons-codec的不同版本)。

使用java decomplier打开commons-codec-1.2.jar,发现这个jar下的Base64.class没有encodeBase64String()方法。

解决方案:

打开项目的pom.xml文件,选择Dependency Hierarchy(依赖层次),找到commons-codec-1.2.jar,发现该jar是被某个框架默认引入的,而不是在pom.xml通过标签明文引入的。所以,将commons-codec-1.2.jar从相关框架中exclude。

NoSuchMethodError通用解决方案_第2张图片

 

总结

NoSuchMethodError这种错误必定是相关的class加载不对,定位到加载的哪个jar下面的class是特别重要的。定位代码如下(仅作参考)

ClassLoader classloader = ClassA.class.getClassLoader();
URL res = classloader.getResource("path of ClassA");
// eg:org/apache/xmlbeans/XmlOptions.class

String path = res.getPath();
LOGGER.debug("ClassA.class loaded from " + path)

你可能感兴趣的:(问题及解决方案)