你們在項目中確定有碰到過Maven
的Jar包衝突問題,常常出現的場景爲:apache
本地運行報NoSuchMethodError
,ClassNotFoundException
。明明在依賴裏有這個Jar包啊。怎麼運行不了!?微信
項目中明明定義着某個jar包版本爲2.0.2
,怎麼打包以後變成2.5.0
了!?maven
A項目引xxx.jar包運行好好的,B項目一樣引入xxx.jar後,運行報錯了。。是B項目有問題,仍是xxx.jar包有問題!?性能
本地環境和測試環境運行的好好的,到了生產就報一堆NoSuchMethodError
,是我人品有問題仍是生產環境有問題!?測試
這樣的問題若是不熟悉maven
依賴機制的同窗排查起來,估計挺頭痛的。spa
並且maven
依賴結構很差的項目,在引入新的Jar包時的風險也是巨大的。小則影響性能,大則引發生產發佈和運行時異常。插件
其實以上問題的根源都來自於Maven
的Jar包衝突和使用不當的依賴傳遞。這篇文章我就好好分析下如下3個內容:命令行
POM
文件幾乎全部的Jar包衝突都和依賴傳遞原則有關,因此咱們先說Maven
中的依賴傳遞原則:code
最短路徑優先原則xml
假如引入了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版本的。
假設咱們項目中依賴了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
用這個插件能很好的顯示出項目中全部的依賴樹和衝突
這裏面紅色高亮的部分,就代表這個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
參數,要否則是不會顯示被忽略的包的
其實mvn命令行同樣好用。很是清晰明確。
排除法
仍是上面的那個例子,如今生效的是2.5.0,若是想生效4.0.1。只須要在2.5.0上面點exclude
就好了。
版本鎖定法
若是不少個依賴都傳遞了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個依賴傳遞的原則,優先級爲最高
鎖定版本後,依賴樹爲:
都統一變成4.0.1,鎖定版本有一個好處:版本鎖定並不排除Jar包,並且顯示的把全部版本不一致的Jar包變成統一一個版本,這樣在閱讀代碼時比較友好。也不用忍受一大堆的exclude
標籤。
POM
文件我本人是有些輕度代碼潔癖的人,因此即使是pom文件的依賴關係也想幹淨而整潔。如何寫好乾淨的POM呢,做者認爲有幾點技巧要注意:
<dependencyManagement>
,來進行本項目一些依賴版本的管理,這樣能夠從很大程度上解決必定的衝突mvn dependency:analyze-only
命令用於檢測那些聲明瞭可是沒被使用的依賴,若有有一些是你本身聲明的,那儘可能去掉mvn dependency:analyze-duplicate
命令用來分析重複定義的依賴,清理那些重複定義的依賴其實龐大的項目依賴傳遞也必定多。可是無論多複雜的依賴關係,看到不要懼怕。就這麼幾條原則,細心的去分析,全部的依賴都有跡可循。
這些傳遞依賴若是管理的好,能讓你的維護成本大大下降。若是管很差,這羣野孩子每個均可能是引起下一個NoSuchMethodError
的導火索。
若是你喜歡做者的文章,歡迎微信公衆號關注 「元人部落」
一個只作原創的技術科技分享號