1. 程式人生 > 其它 >【JavaDebug(十二)】之NoSuchMethodError,使用arthas工具查詢jar包,maven命令搜尋jar包

【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>

至此,問題解決!!!