java.lang.NoSuchMethodError问题分析

1、问题引出:

今天在项目中,发现报了java.lang.NoSuchMethodError这个错误 ,错误信息以下:
Caused by: java.lang.NoSuchMethodError: com.google.common.base.Objects.firstNonNull(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
    at com.google.common.cache.CacheBuilder.getKeyStrength(CacheBuilder.java:529)java

根据异常信息咱们大概能够了解,工程中引用了一个名叫 com.google.common.cache.CacheBuilder 的类,并调用了其中名为 getKeyStrength 的方法,以下:web

Strength getKeyStrength() {
    return (Strength)Objects.firstNonNull(this.keyStrength, Strength.STRONG);
}

该方法又调用了com.google.common.base.Objects.firstNonNull这个方法,可是系统在加载该方法时却表示没有找到该方法。在IDEA里面输入com.google.common.base.Objects这个类名出来了两个:缓存

点进去发现,guava-14.0.1的com.google.common.base.Objects类实现是有这个方法的,以下:maven

public static <T> T firstNonNull(@Nullable T first, @Nullable T second) {
    return first != null ? first : Preconditions.checkNotNull(second);
}

可是在google-collection-1.0.jar包中的com.google.common.base.Objects却没有这个实现,其内容以下:ide

package com.google.common.base;

import com.google.common.annotations.GwtCompatible;
import java.util.Arrays;
import javax.annotation.Nullable;

@GwtCompatible
public final class Objects {
    private Objects() {
    }

    public static boolean equal(@Nullable Object a, @Nullable Object b) {
        return a == b || a != null && a.equals(b);
    }

    public static int hashCode(Object... objects) {
        return Arrays.hashCode(objects);
    }
}

得出结论,这是一个包冲突问题,工具

2、问题解决:

当遇到这类问题咱们该如何解决呢,主要有如下三步:测试

一、 发现是哪一个类发生了冲突:

前面已经分析过,是com.google.common.base.Objects这个类的冲突ui

二、发现冲突 jar 包,即冲突类存在于哪一个 Jar 包中:

见前面分析,发现是google-collection-1.0.jar和guava-14.0.1.jar包冲突this

三、发现这个冲突 Jar 包是自身系统直接引用的仍是系统引用的 Jar 间接引用的:

第三步,咱们看 google-collections-1.0.jar 是不是应用直接引用的。google

经过查看这两个包的引用发现,工程里面直接引用了google-collections这个jar包,以下:

<dependency>
    <groupId>com.google.collections</groupId>
    <artifactId>google-collections</artifactId>
</dependency>

,没有发现直接引用guava.jar包,所以guava.jar包是经过其余jar包间接引用过来的。

咱们的项目是经过 maven 进行引用 jar 包的管理。所以,结合 maven 的命令 mvn dependency:tree 能够发现这两个jar 包究竟是经过哪些jar 包间接引用进来的。

       经查询资料发现,guava.jar包是google-collections的升级版,包括后者,因此去掉google-collections的包引入,重启工程,问题解决。

3、问题分析与拓展

      这类状况的发生极可能是由于以下情况引发的:随着业务需求的不断扩展,应用中代码量也会逐渐增加,工程中引用的二方包或者三方包也天然而然会愈来愈多。所以,不可避免,可能存在引用的二方包或三方包相互冲突所致使的系统问题。

通常经过以下方法,能够定位并决绝包冲突的问题:

一、mvn dependency:tree

这个命令能够输出全部的依赖,若是加入一些参数,能够过滤一些东西,如

-Dverbose

-Dincludes

-Dexcludes  等参数会可以帮助尽快定位问题

二、IDEA的jar包依赖分析,再次再也不赘述

三、本身写一个测试类,来查看加载的究竟是哪一个jar包:


有时,你觉得解决了,可是恰恰仍是报类包冲突(典型症状是java.lang.ClassNotFoundException或Method不兼容等异常),这时你能够设置一个断点,在断点处经过下面这个我作的工具类来查看Class所来源的JAR包:

    package com.ridge.util;  
      
    import java.io.File;  
    import java.net.MalformedURLException;  
    import java.net.URL;  
    import java.security.CodeSource;  
    import java.security.ProtectionDomain;  
      
    /** 
     * @author : chenxh 
     * @date: 13-10-31 
     */  
    public class ClassLocationUtils {  
      
        /** 
         * 获取类全部的路径 
         * @param cls 
         * @return 
         */  
        public static String where(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();  
            if (pd != null) {  
                final CodeSource cs = pd.getCodeSource();  
                if (cs != null) result = cs.getLocation();  
                if (result != null) {  
                    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) {  
                final ClassLoader clsLoader = cls.getClassLoader();  
                result = clsLoader != null ?  
                        clsLoader.getResource(clsAsResource) :  
                        ClassLoader.getSystemResource(clsAsResource);  
            }  
            return result.toString();  
        }  
      
    }  


随便写一个测试,设置好断点,在执行到断点处按alt+F8动态执行代码(intelij idea),假设咱们输入:
Java代码  收藏代码

ClassLocationUtils.where(org.objectweb.asm.ClassVisitor.class)  

便可立刻查出类对应的JAR了:

这就是org.objectweb.asm.ClassVisitor类在运行期对应的JAR包,若是这个JAR包版本不是你指望你,就说明是你的IDE缓存形成的,这时建议你Reimport一下maven列表就能够了,以下所示(idea):

四、有的时候多是IDE的问题:

(1)Reimport一下,IDE会强制根据新的pom.xml设置从新分析并加载依赖类包,以获得和pom.xml设置相同的依赖。
(2)idea清除缓存,采用idea自带的功能,File->Invalidate Caches 功能直接完成清除idea cache

参考文献:http://fufeng.iteye.com/blog/1755167

http://blog.csdn.net/sun_wangdong/article/details/51852113

相关文章
相关标签/搜索