java.lang.NoSuchMethodError问题分析

一、问题引出:

今天在项目中,发现报了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)

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

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

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

195029_k5Bx_3729778.png

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

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却没有这个实现,其内容如下:

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);
    }
}

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

二、问题解决:

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

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

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

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

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

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

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

通过查看这两个包的引用发现,工程里面直接引用了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的包引入,重启工程,问题解决。

三、问题分析与拓展

      这类情况的发生很可能是因为如下状况引起的:随着业务需求的不断扩展,应用中代码量也会逐渐增长,工程中引用的二方包或者三方包也自然而然会越来越多。因此,不可避免,可能存在引用的二方包或三方包相互冲突所导致的系统问题。

一般通过如下方法,可以定位并决绝包冲突的问题:

1、mvn dependency:tree

这个命令可以输出所有的依赖,如果加入一些参数,可以过滤一些东西,如

-Dverbose

-Dincludes

-Dexcludes  等参数会能够帮助尽快定位问题

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

3、自己写一个测试类,来查看加载的到底是哪个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):

4、有的时候可能是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

转载于:https://my.oschina.net/u/3729778/blog/1581781

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值