用好這幾個技巧,解決Maven Jar包沖突易如反掌


前言

大家在項目中肯定有碰到過Maven的Jar包沖突問題,經常出現的場景為:

本地運行報NoSuchMethodErrorClassNotFoundException。明明在依賴里有這個Jar包啊。怎么運行不了!?

項目中明明定義着某個jar包版本為2.0.2,怎么打包之后變成2.5.0了!?

A項目引xxx.jar包運行好好的,B項目同樣引入xxx.jar后,運行報錯了。。是B項目有問題,還是xxx.jar包有問題!?

本地環境和測試環境運行的好好的,到了生產就報一堆NoSuchMethodError,是我人品有問題還是生產環境有問題!?

這樣的問題如果不熟悉maven依賴機制的同學排查起來,估計挺頭痛的。

而且maven依賴結構不好的項目,在引入新的Jar包時的風險也是巨大的。小則影響性能,大則引起生產發布和運行時異常。

其實以上問題的根源都來自於Maven的Jar包沖突和使用不當的依賴傳遞。這篇文章我就好好分析下以下3個內容:

  • 依賴傳遞的原則和產生Jar包沖突的原理分析
  • 定位沖突以及解決Jar包沖突的幾個簡單技巧
  • 如何寫一個干凈依賴關系的POM文件

依賴傳遞原則

幾乎所有的Jar包沖突都和依賴傳遞原則有關,所以我們先說Maven中的依賴傳遞原則:

最短路徑優先原則

假如引入了2個Jar包A和B,都傳遞依賴了Z這個Jar包:

A -> X -> Y -> Z(2.5)

B -> X -> Z(2.0)

那其實最終生效的是Z(2.0)這個版本。因為他的路徑更加短。如果我本地引用了Z(3.0)的包,那生效的就是3.0的版本。一樣的道理。

最先聲明優先原則

如果路徑長短一樣,優先選最先聲明的那個。

A -> Z(3.0)

B -> Z(2.5)

這里A最先聲明,所以傳遞過來的Z選擇用3.0版本的。

Jar包沖突的原理

假設我們項目中依賴了A和B兩個Jar包。而A和B各自又有以下傳遞依賴

A -> X -> Z(2.0)

B -> X -> Y -> Z(2.5)

那最終系統中Z包就產生了沖突,2.0和2.5兩個版本沖突。但是classpath中只會依賴一個版本的Z包。根據傳遞依賴的最短路徑優先原則,最終依賴的應該是2.0版本。

如果Y包中用了Z包2.5版本中新的method時候,當運行到這段邏輯的時候。就會報NoSuchMethodError了。因為本來依賴的是2.5版本,但是因為Jar包沖突Maven選擇了2.0版本,2.0版本中又沒有這個新的method,導致出錯。

但要注意的是,不是所有沖突都會引起運行異常。相反,大部分公司的項目都會有一些Jar包沖突,但其實沒有造成運行時的問題。

這是因為很多傳遞依賴的Jar包,不管是2.0版本也好,2.5版本也好,都可以運行。

只有高版本Jar包不向下兼容,或者新增了某些低版本沒有的API才有可能導致這樣的問題

定位沖突

IDEA提供了一個maven依賴分析神器:Maven Helper

file

用這個插件能很好的顯示出項目中所有的依賴樹和沖突

file

這里面紅色高亮的部分,就表明這個Jar包有了沖突。選中這個jar包,可以看到這2個版本的沖突的來源。

上圖的例子,表明cruator-client這個Jar包,有2個傳遞依賴,分別為2.5.0版本和4.0.1版本。沖突的描述為:

omitted for conflict with 2.5.0. 由於與2.5.0版本沖突而被省略

具體的層級在右邊也一目了然了,所以maven最終根據最短路徑優先原則選擇了2.5.0版本,4.0.1版本被忽略。

這時候有同學會問:本地環境我可以利用Maven Helper來定位,那么預生產或者生產環境呢。又沒有IDEA,如何定位沖突的細節?

可以利用mvn命令來解決:

mvn dependency:tree -Dverbose

此處一定不要省略-Dverbose參數,要不然是不會顯示被忽略的包的

file

其實mvn命令行一樣好用。非常清晰明確。

解決Jar包沖突的幾個實用技巧

排除法

還是上面的那個例子,現在生效的是2.5.0,如果想生效4.0.1。只需要在2.5.0上面點exclude就行了。

file

版本鎖定法

如果很多個依賴都傳遞了Jar包A,涉及了很多個版本,但是你只想指定一個版本。用排除法一個個去exclude太麻煩,而且exclude在pom文件中也會體現,太多的話,也影響代碼整潔和閱讀感受。

這時候需要用到版本鎖定法

何謂版本鎖定法?公司的項目一般都會有父級pom,你想指定哪個版本只需要在你項目的父POM中(當然在本工程內也可以)定義如下:(還是舉上個例子,指定4.0.1版本)

<dependencyManagement>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-client</artifactId>
        <version>4.0.1</version>
    </dependency>
</dependencyManagement>

鎖定版本法可以打破2個依賴傳遞的原則,優先級為最高

鎖定版本后,依賴樹為:

file

都統一變成4.0.1,鎖定版本有一個好處:版本鎖定並不排除Jar包,而且顯示的把所有版本不一致的Jar包變成統一一個版本,這樣在閱讀代碼時比較友好。也不用忍受一大堆的exclude標簽。

如何寫一個干凈依賴關系的POM文件

我本人是有些輕度代碼潔癖的人,所以即便是pom文件的依賴關系也想干凈而整潔。如何寫好干凈的POM呢,作者認為有幾點技巧要注意:

  • 盡量在父POM中定義<dependencyManagement>,來進行本項目一些依賴版本的管理,這樣可以從很大程度上解決一定的沖突
  • 如果是提供給別人依賴的Jar包,盡可能不要傳遞依賴不必要的Jar包
  • 使用mvn dependency:analyze-only命令用於檢測那些聲明了但是沒被使用的依賴,如有有一些是你自己聲明的,那盡量去掉
  • 使用mvn dependency:analyze-duplicate命令用來分析重復定義的依賴,清理那些重復定義的依賴

最后

其實龐大的項目依賴傳遞也一定多。但是不管多復雜的依賴關系,看到不要害怕。就這么幾條原則,細心的去分析,所有的依賴都有跡可循。

這些傳遞依賴如果管理的好,能讓你的維護成本大大降低。如果管不好,這群野孩子每一個都可能是引發下一個NoSuchMethodError的導火索。

關注作者

如果你喜歡作者的文章,歡迎微信公眾號關注 「元人部落
一個只做原創的技術科技分享號

關注后回復“資料”獲取50G的技術資料

file


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM