首頁>技術>

上篇文章中,小黑哥分析 Maven 依賴衝突分為兩類:

專案同一依賴應用,存在多版本,每個版本同一個類,可能存在差異。專案不同依賴應用,存在包名,類名完全一樣的類。

第二種情況,往往是這個場景,本地/測試環境執行的都是好好的,上線之後測試就是不行。

這其實與 JVM 類載入有關,本地/測試環境載入正確類,而生產環節載入錯的類,為什麼會這樣?

主要有兩個原因:

同一個類只會被載入器載入一次不同環境,類的載入順序不同同一個類只會被載入器載入一次

JVM 類載入具有快取機制,每個類載入的時候首先檢查一遍,類是否被當前類載入器載入。若未被載入,先交給其父類載入器載入,父類載入器不能載入,才會交給當前類載入器。

當前類載入器載入完成之後,將會將其快取起來。

類載入的核心原始碼位於 ClassLoader#loadClass:

① 處將會檢查ClassLoader#findLoadedClass 最終將會呼叫 ClassLoader#findLoadedClass0,這是一個 native 方法,最終將會根據類名加類載入器為鍵值查詢快取。

每個類載入器負責的載入範圍都不一樣:

BootstrapClassLoader 引導類載入載入最核心的類庫,如 $JAVA_HOME/jre/lib/ExtClassLoader 擴充套件類載入器負責載入$JAVA_HOME/jre/lib/ext下的一些擴充套件類AppClassLoader 應用類載入器將載入 classpath 指定的類。

我們執行的應用依賴的各種類,一般將會由 AppClassLoader 記載,同名類被載入後,下次碰到就不會再被載入。

畫外音:利用快取加快查詢速度

不同環境,類的載入順序不同

Java 可以使用 -classpath 引數指定依賴類所在位置。

類的載入順序可以通過以下方式指定:

java -classpath a.jar:b.jar:c.jar xx.xx.Main

上面這種方式,類載入首先會從 a.jar 中查詢相關類,找不到才會繼續往後查詢。所以可以通過這種方式可以指定使用哪個 jar 包內同名類。

但是這種方式有點繁瑣,如果依賴 100 個 jar 包,需要全部寫上去。

所以生產環境可以使用使用 shell 命令將 jar 拼接起來:

LIB_DIR=libLIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\\n" ":"`

另外 java 支援萬用字元的寫法:

java -classpath './*' xx.xx.Main

這種方式的載入順序將會受到底層系統檔案載入順序影響。

復現依賴衝突

假設我們現在應用依賴如下:

A 應用依賴 B、C,且 B,C 中存在同包同名類 org.example.App,程式碼如下:

如果指定 jar 包順序啟動應用:

# A,B,C 放置同一資料夾下java -classpath A-1.0-SNAPSHOT.jar:B-1.0-SNAPSHOT.jar:C-1.0-SNAPSHOT.jar org.example.ClassA

日誌輸出如下:

改變 B ,C 順序:

類載入器的類的查詢順序將會通過 classpath 指定順序從前往後查詢。

如果使用萬用字元啟動:

java -classpath './*' org.example.ClassA

這種情況 jvm 到底載入那個類就成了薛定諤的類了,執行之前無法確定載入類來自哪個 jar 包。

使用 verbose:class 列印載入類

我們可以在 jvm 啟動指令碼加入如下引數 -verbose:class,然後重啟,日誌裡會打印出每個類的載入資訊。

java -verbose:class -classpath './*' xx.xx.Main

日誌輸出如下:

不過這種方式需要重啟應用,對生產系統來說,影響還是比較大,不太優雅。

Arthas 查到來源類

阿里開源專案 Arthas sc 命令可以用來查詢載入類的資訊。。

sc 命令是 Search-Class 簡寫,這個命令能搜尋出已經載入到 JVM 中的 Class 資訊,支援引數如下表格所示。

程式啟動之後,啟動 arthas,進入 A 應用。

執行如下命令:

sc -d org.example.App

輸出結果如下 :

code-source 顯示當前查詢類 org.example.App 來自的 C。

另外我們可以 jad 命令反編譯類,線上檢視原始碼。

總結

這篇文章主要解釋應用中存在多個同名類,環境不同,類載入不同的原因。接著介紹了兩種快速查詢執行應用依賴類來源的方法。

當定位到了衝突類的來源,我們可以顯示指定 classpath jar 包的順序,指定類載入的順序。但這只是暫時解決問題。本質上依賴衝突的問題,還是需要深層次排除的。

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 搭建websocket訊息推送服務,必須要考慮的幾個問題