java.lang.NoSuchMethodError 如何定位和解决

2,625 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

日常开发过程中再引入其他jar包的时候最容易遇到的异常java.lang.NoSuchMethodError,该如何定位和解决,本文统一整理。

1、直接点进引入的类查看是否有相关的方法,一般都是存在要不然编译就报错了

2、进入类之后查看类的版本,idea中可以直接在项目中查看依赖jar包版本,如下图所示:

1669779325944.jpg

查看引入jar包版本是否有冲突,使用idea的插件分析jar包冲突问题,如果有冲突的jar包,直接排除jar包冲突,重新刷新maven后重试,一般这种场景下就能够解决80%的异常场景。

1669779357699.jpg

3、上述两种方案都没用,debugger相关方法,查看对应的类确实没有相关的method,说明实际运行使用的jar版本不对,这个时候我们就要看具体运行的jar版本是哪一个了(终极解决方案:直接删除web下编译的target,重新编译,如果没有jar包冲突的话就直接解决了,我的是这样解决的,如下图查看lib下的jar的版本是否正确),后面代码上可验证一下具体的编译版本是实际运行版本不一致的问题:

1669779369792.jpg

上代码,通过URL 类加载器打印找不到方法的类的全路径:

// 工具类中定义URL的getClassLocation方法 获取路径
public static URL getClassLocation(final Class cls) {
        if (cls == null)throw new IllegalArgumentException("null input: cls");
        URL result = null;
        final String clsAsResource = cls.getName().replace('.', '/').concat(".class");
        final ProtectionDomain pd = cls.getProtectionDomain();
        // java.lang.Class contract does not specify if 'pd' can ever be null;
        // it is not the case for Sun's implementations, but guard against null
        // just in case:
        if (pd != null) {
            final CodeSource cs = pd.getCodeSource();
            // 'cs' can be null depending on the classloader behavior:
            if (cs != null) result = cs.getLocation();
            if (result != null) {
                // Convert a code source location into a full class file location
                // for some common cases:
                if ("file".equals(result.getProtocol())) {
                    try {
                        if (result.toExternalForm().endsWith(".jar") ||
                                result.toExternalForm().endsWith(".zip"))
                            result = new URL("jar:".concat(result.toExternalForm())
                                    .concat("!/").concat(clsAsResource));
                        else if (new File(result.getFile()).isDirectory())
                            result = new URL(result, clsAsResource);
                    }
                    catch (MalformedURLException ignore) {}
                }
            }
        }
        if (result == null) {
            // Try to find 'cls' definition as a resource; this is not
            // document.d to be legal, but Sun's implementations seem to         //allow this:
            final ClassLoader clsLoader = cls.getClassLoader();
            result = clsLoader != null ?
                    clsLoader.getResource(clsAsResource) :
                    ClassLoader.getSystemResource(clsAsResource);
        }
        return result;
    }
​

具体代码中输出对应的路径

System.out.print(getClassLocation(OutboundNoticeRo.class));

输出的结果如下:

jar:file:/Users/local/apache-tomcat-9.0.54/webapps/canghai_deploy_war/WEB-INF/lib/scf-goods-facade-P637-1.2.6-20220418.062334-6.jar!/com/ncarzone/scf/goods/facade/entity/outbound/ro/OutboundNoticeRo.class
​
​

根据输出的结果发现:实际编译版本应该是P722-01-michelin-RELEASE版本,但是运行使用的却是P637-1.2.6-20220418.062334-6.jar版本,此时发现webapp下target下的lib版本不对导致的,直接删除target后重新编译执行解决。