天天看點

沖突 tomcat 優先加載_一次Maven依賴沖突踩坑,把依賴調解、類加載徹底整明白了...1. Maven依賴沖突經曆2. 為什麼需要Maven依賴調解3. 排查線上問題的利器-Arthas4. 參考

來源 | 草捏子,作者 | 草捏子

之前有了解到Maven可以依賴調解,即當包版本不一緻時,會根據一定規則選擇相應的包來加載,進而避免沖突。當時不解的是既然Maven都能解決沖突,為何還經常聽到“發生了依賴沖突”,沖突不是解決了嗎,還存在什麼問題呢?直到這周在工作中自己遇到了,就明白是咋回事了。下面先從我的實際經曆說起。

1. Maven依賴沖突經曆

我在Y子產品中,寫了一個

Encryptor

類,主要是使用了

DigestUtils

MessageDigest

HmacUtils

等類對字元串進行加密(下面代碼是随便寫的,隻表示使用到了這些類),如下:

執行單元測試可以正常運作。但是當整個應用啟動時,則會報

IllegalAccessError

錯誤。

沖突 tomcat 優先加載_一次Maven依賴沖突踩坑,把依賴調解、類加載徹底整明白了...1. Maven依賴沖突經曆2. 為什麼需要Maven依賴調解3. 排查線上問題的利器-Arthas4. 參考

應用啟動報錯IllegalAccessError

在Y子產品下的單元測試運作時不會報錯,但是當整個應用啟動,作為程式入口的X子產品,調用Y子產品中的

Encryptor

時,發生了

IllegalAccessError

報錯。根據圖中的具體報錯資訊,是說沒有權限通路

getSha256Digest

方法,我Ctrl+B點進

getSha256Digest

方法檢視,如下:

沖突 tomcat 優先加載_一次Maven依賴沖突踩坑,把依賴調解、類加載徹底整明白了...1. Maven依賴沖突經曆2. 為什麼需要Maven依賴調解3. 排查線上問題的利器-Arthas4. 參考

getSha256Digest是public

getSha256Digest

方法是

public

的通路級别,我一臉懵。由于這個方法很簡單,既然報錯,那我就索性不用了,換成下面這種寫法。

又報錯了,好吧,真是躲不過了!報錯如下:

沖突 tomcat 優先加載_一次Maven依賴沖突踩坑,把依賴調解、類加載徹底整明白了...1. Maven依賴沖突經曆2. 為什麼需要Maven依賴調解3. 排查線上問題的利器-Arthas4. 參考

應用啟動報錯ClassNotFoundException

這次報的是

ClassNotFoundException

HmacUtils

這個類找不到。可是我Ctrl+B進去,這個類好好的就在那裡啊。這時我才把注意力集中在思考是不是發生了Maven依賴沖突。我打開pom.xml,用Dependency Analyzer檢視,果然我使用的

commons-codec

包發生了沖突。

沖突 tomcat 優先加載_一次Maven依賴沖突踩坑,把依賴調解、類加載徹底整明白了...1. Maven依賴沖突經曆2. 為什麼需要Maven依賴調解3. 排查線上問題的利器-Arthas4. 參考

X子產品的依賴樹

在Y子產品中,依賴關系:Y -> B -> C -> commons-codec-1.10。而在X子產品中,引用了A包:X -> A -> commons-codec-1.6,也引用了Y子產品:X -> Y -> B -> C -> commons-codec-1.10。可見

commons-codec

包有兩個版本1.6和1.10,是以Maven會進行依賴調解,第一原則是“路徑最短者優先”,自然隻會使用1.6版本的包。而我再去檢視1.6的包下,

getSha256Digest

方法是

private

的通路級别,

HmacUtils

這個類也不存在。解釋了之前的報錯。解決該沖突,通過排除依賴便能解決了,将A包下的

commons-codec

排除,如下:

排除後,這時将隻有1.10版本的包,程式也可以正常運作了。

2. 為什麼需要Maven依賴調解

問題已經解決了,大家是不是也明白了,為什麼依賴沖突會常導緻發生

NoClassDefFoundError

NoSuchMethodException

IllegalAccessError

等錯誤。雖然Y子產品在編譯時,由于引入了

commons-codec 1.10

能正常編譯,但是在運作時,由于依賴沖突,隻加載了1.6版本的包,是以不能正常運作。

注意:代碼的編譯僅僅是編譯目前的代碼。編譯成功後,最後能否正常運作,還要取決于運作時的環境是否等同或相容編譯時環境。

下面我們想想為什麼需要Maven依賴調解,如果不調解行不行。

當使用Maven的過程中,如果同時引入了groupId和artifactId相同而version不同的包時,Maven會認為發生了依賴沖突,将進行依賴調解,通過兩個原則決定使用哪個版本的包:第一原則,路徑最近者優先,如前文。如果路徑相同,則使用第二原則,在pom中第一聲明者優先。而當我們在點選Run運作時,classpath中将隻會有一個明确版本的包。

思考一下。Java在運作時,是否能引入版本不同的包。其實這個問題是在問,java指令的classpath參數中能不能有多個版本不同的包,當然是可以的。classpath參數是用于訓示JVM如何搜尋class檔案,當你在classpath中指定的路徑下有多個版本不同的包,JVM都會去jar包下搜尋class檔案進行加載,而至于class能不能成功加載,則在于ClassLoader的邏輯,當同名類被加載時,則不會再被加載,即同一個類隻會被加載一次。這也意味,當有多個版本不同的包時,包在classpath中的順序,決定了哪個包中的類能先被加載。而這樣具有不确定性。因為在生産環境下通常使用

shell

指令将jar包拼接:

不同環境下得到的jar包順序可能是不同的。而Maven依賴調解将使得隻有一個明确版本的包參與建構,進而避免不确定性。

3. 排查線上問題的利器-Arthas

Arthas,早有聽說,但一直未使用過,這次我嘗試了下,覺得确實可以,安利下。對于前文說的依賴沖突情況,當發生

IllegalAccessError

報錯時,可以通過Arthas直接檢視運作情況下的

DigestUtils

。我們把代碼變為最初的情況,且在Main類中加個死循環,為了讓程式不死掉,以通過Arthas觀察。

打開Arthas,連接配接上我們的程式(可以通過官方教程學習),然後通過

sc

指令檢視

DigestUtils

可以從

code-source

中清晰地查到

DigestUtils

是哪個包下的Class,這時就該意識到發生了依賴沖突問題。

而通過

jad

指令,還能反編譯,線上看代碼。好用!

4. 參考

  1. Arthas 實戰,助你解決同名類依賴沖突問題(https://www.cnblogs.com/goodAndyxublog/p/12424734.html)
  2. Maven依賴沖突問題原理簡析(https://blog.csdn.net/qq_27529917/article/details/79741607)
  3. 重新看待Jar包沖突問題及解決方案(http://www.yangbing.club/2017/07/15/solution-for-jar-conflicts/)