今天在項目中,發現報了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); } }
得出結論,這是一個包衝突問題,工具
當遇到這類問題咱們該如何解決呢,主要有如下三步:測試
前面已經分析過,是com.google.common.base.Objects這個類的衝突ui
見前面分析,發現是google-collection-1.0.jar和guava-14.0.1.jar包衝突this
第三步,咱們看 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的包引入,重啓工程,問題解決。
這類狀況的發生極可能是由於以下情況引發的:隨着業務需求的不斷擴展,應用中代碼量也會逐漸增加,工程中引用的二方包或者三方包也天然而然會愈來愈多。所以,不可避免,可能存在引用的二方包或三方包相互衝突所致使的系統問題。
通常經過以下方法,能夠定位並決絕包衝突的問題:
這個命令能夠輸出全部的依賴,若是加入一些參數,能夠過濾一些東西,如
-Dverbose
-Dincludes
-Dexcludes 等參數會可以幫助儘快定位問題
有時,你覺得解決了,可是恰恰仍是報類包衝突(典型症狀是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):
(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