【JavaDebug(十二)】之NoSuchMethodError,使用arthas工具查詢jar包,maven命令搜尋jar包
技術標籤:JavaDebugJava學習筆記# JavaEE反編譯javaspringmaven
本文章由公號【開發小鴿】釋出!歡迎關注!!!
老規矩–妹妹鎮樓:
一. BUG描述
本質就是一個NoSuchMethodError,如下所示:
java.lang.NoSuchMethodError: org.springframework.core.annotation.AnnotationAwareOrderComparator.sort(Ljava/util/List;)V
at org.springframework.boot.SpringApplication. getSpringFactoriesInstances(SpringApplication.java:394)
at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:383)
at org.springframework.boot.SpringApplication.initialize(SpringApplication.java:249)
at org.springframework.boot.SpringApplication. <init>(SpringApplication.java:225)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
at com.example.demoNoSuchMethodError.DemoNoSuchMethodErrorApplication.main(DemoNoSuchMethodErrorApplication. java:13)
這個異常的意思是AnnotationAwareOrderComparator類缺少sort(Ljava/util/List;)V這個函式,後面的函式使用的是JVM中的函式表示方式。
二. 解決思路
(一). 概述
某個包的某個類缺少某個函式,這貌似不是我們的問題,可能是某個包的版本原因導致這個包的這個類沒有這個函式,也有可能是這個包的優先順序高於同名的其他包,導致其他包被覆蓋了,因此我們需要找到這個包的位置。
(二) arthas工具
那麼,如何找到這個類是從哪個包引入的呢?使用阿里開源的arthas工具來解決,它能夠解決以下的問題:
1. 這個類從哪個 jar 包載入的?為什麼會報各種類相關的 Exception?
2. 我改的程式碼為什麼沒有執行到?難道是我沒 commit?分支搞錯了?
3. 遇到問題無法在線上 debug,難道只能通過加日誌再重新發布嗎?
4. 線上遇到某個使用者的資料處理有問題,但線上同樣無法 debug,線下無法重現!
5. 是否有一個全域性視角來檢視系統的執行狀況?
6. 有什麼辦法可以監控到JVM的實時執行狀態?
通過這個工具來檢視這個類是從哪個包引入的,但是首先我們要保證在使用該工具時出現異常的類所在的程序依然在執行,這就需要我們在可能出現異常的程式碼塊中使用try-catch語句塊來抓住異常,這裡使用Throwable類物件而不是Exception類物件來catch,可能是因為如果使用Exception類的話,由於報錯的是一個Error,它無法catch到這個Error,因此會直接報錯並且退出,而Throwable類是Error類和Exception類的父類,包含了Error類,因此可以catch這個Error,進入catch程式碼塊中,執行阻塞的語句,保持程序的執行,這樣arthas工具可以在程序執行時找出錯誤。
下載完arthas的jar包後,新建一個資料夾將jar包放進去。之後可以在IDEA的控制檯使用,也可以在CMD中使用,進入該資料夾中,輸入
java -jar arthas-boot.jar
arthas會掃描JVM啟動的程序,我們可以選擇對應的程序,進入該程序中,並執行sc命令來查詢
sc -d org.springframework.core.annotation.AnnotationAwareOrderComparator
可以看到輸出如下所示,spring-2.5.6jar包就被找出來了:
$ sc -d org.springframework.core.annotation.AnnotationAwareOrderComparator
class-info org.springframework.core.annotation.AnnotationAwareOrderComparator
code-source /Users/hengyunabc/.m2/repository/org/springframework/spring/2.5.6.SEC03/spring-2.5.6.SEC03.jar
name org.springframework.core.annotation.AnnotationAwareOrderComparator
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name AnnotationAwareOrderComparator
modifier public
annotation
interfaces
super-class +-org.springframework.core.OrderComparator
+-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@5c647e05
+-sun.misc.Launcher$ExtClassLoader@689e3d07
classLoaderHash 5c647e05
Affect(row-cnt:1) cost in 41 ms.
使用jad命令檢視反編譯的AnnotationAwareOrderComparator原始碼:
$ jad org.springframework.core.annotation.AnnotationAwareOrderComparator
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@5c647e05
+-sun.misc.Launcher$ExtClassLoader@689e3d07
Location:
/Users/hengyunabc/.m2/repository/org/springframework/spring/2.5.6.SEC03/spring-2.5.6.SEC03.jar
/*
* Decompiled with CFR 0_132.
*/
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
public class AnnotationAwareOrderComparator
extends OrderComparator {
protected int getOrder(Object obj) {
Order order;
if (obj instanceof Ordered) {
return ((Ordered)obj).getOrder();
}
if (obj != null && (order = obj.getClass().getAnnotation(Order.class)) != null) {
return order.value();
}
return Integer.MAX_VALUE;
}
}
Affect(row-cnt:1) cost in 286 ms.
發現,裡面確實沒有sort函式,終於找到問題所在了,只要將這個jar包排除即可。
(三). maven命令
如何找到這個spring的jar包呢?首先,從Maven命令入手,檢視maven的依賴樹,沒有以依賴衝突,可以使用CTRL + F 來搜尋jar包。也可以直接使用maven命令搜尋指定的包的依賴樹,如搜尋spring包的依賴樹:
mvn dependency:tree -Dincludes=org/springframework/spring
這樣過於麻煩,接著找到一個maven外掛maven-helper,直接在File -> setting -> Plugins -> 搜尋maven helper ,然後點選安裝,重啟IDEA。
Maven Helper成功後,在左下角會多一個tab頁Dependency Analyzer,通過它可以進行如下操作:
1. Conflicts(檢視衝突)
2. All Dependencies as List(列表形式檢視所有依賴)
3. All Dependencies as Tree(樹形式檢視所有依賴)
因此,找到了spring包的位置在新新增的一個包中,那麼直接在該包的座標匯入中使用以下的方式來排除這個spring依賴:
<!-- Canal客戶端-->
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.0.24</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
至此,問題解決!!!