Maven 基礎(二) | 解決依賴衝突的正確姿勢
- 2020 年 2 月 18 日
- 筆記

一、依賴原則
假設,在 JavaMavenService2
模組中,log4j
的版本是 1.2.7
,在 JavaMavenService1
模組中,它雖然繼承於 JavaMavenService2
模組,但是它排除了在 JavaMavenService2
模組中繼承 1.2.7
的版本,自己引入了 1.2.9
的 log4j
版本。
此時,相對於 WebMavenDemo
而言,log4j.1.2.7.jar
的依賴路徑是 JavaMavenService1 >> JavaMavenService2 >> log4j.1.2.7.jar
,長度是 3;而 log4j.1.2.9.jar
的依賴路徑是 JavaMavenService1 >> log4j.1.2.7.jar
長度是 2。
所以 WebMavenDemo
繼承的是 JavaMavenService1
模組中的 log 版本,而不是 JavaMavenService2
中的,這叫路徑優先原則 (誰路徑短用誰)。

依賴原則
此外,在路徑相同的情況下,

路徑相同
這種場景依賴關係發生了變化,WebMavenDemo
項目依賴 Sercive1
和 Service2
,它倆是同一個路徑,那麼誰在 WebMavenDemo
的 pom.xml
中先聲明的依賴就用誰的版本。這叫先定義先使用原則。
比如:先聲明 JavaMavenService1 所以 WebMavenDemo 繼承它的 log4j.1.2.9.jar 依賴
<!--先聲明 JavaMavenService1 所以 WebMavenDemo 繼承它的 log4j.1.2.9.jar 依賴--> <dependency> <groupId>com.nasus</groupId> <artifactId>JavaMavenService1</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>com.nasus</groupId> <artifactId>JavaMavenService2</artifactId> <version>1.0.0</version> </dependency>
- 參考:cnblogs.com/hzg110/p/6936101.html
二、依賴衝突的原因
項目的依賴 service1
和依賴 service2
同時引入了依賴 log4j
。這時,如果依賴 log4j
在 service1
和 service2
中的版本不一致就可能導致依賴衝突。如下圖:

依賴衝突的原因
注意,上面我用的是可能,並不是說滿足上面的條件就一定會發生依賴衝突。因為 maven 遵循上面提到的兩個原則:
- 先定義先使用原則 (路徑層級相同情況下)
- 路徑優先原則 (誰路徑短用誰)
2.1 依賴衝突會報什麼錯?
依賴衝突通常兩個錯:NoClassDefFoundError
或 NoSuchMethodError
,逐一講解下導致這兩種錯誤的原因:
- 以上圖依賴關係為例,假設
WebDemo
通過排除service1
中低版本的依賴,從而繼承service2
中的高版本的依賴。這時,如果WebDemo
在執行過程中調用log4j(1.2.7)
有,但是升級到log4j(1.2.9)
就缺失的類log
,就會導致運行期失敗,出現很典型的依賴衝突時的NoClassDefFoundError
錯誤。 - 還是以上圖依賴關係為例,
WebDemo
通過排除service1
中低版本的依賴,從而繼承service2
中的高版本的依賴。WebDemo
調用了原來log4j(1.2.7)
中有的方法log.info()
,但升級到log4j(1.2.7)
後,log.info()
不存在了,就會拋出NoSuchMethodError
錯誤.
所以說,當存在依賴衝突時,僅指望 maven 的兩個原則來解決是不成熟的。不管是路徑優先原則還是先定義先使用原則,都有可能造成以上的依賴衝突。那麼如何解決它呢?
三、解決依賴衝突
通過上面的分析我們應該能理解到,解決依賴衝突的核心就是使衝突的依賴版本統一,而且項目不報錯。
我們可以通過運行 maven 命令:mvn dependency:tree
查看項目的依賴樹分析依賴,看那些以來有衝突,還是以上圖舉例:運行命令之後,查看依賴樹的 log4j 依賴就會得到錯誤提示:(1.2.7 omitted for conflict with 1.2.9)
知道了如何查看衝突之後,就是解決衝突。
1、嘗試升高 service2
的版本使他依賴的 log
版本與 service1
的 log
版本一致,但它可能會導致 service2
不能工作。
2、如果 service2
是箇舊項目,找遍了也沒找到與 service1
版本一致的 log
,這時可以嘗試拉低 service1
的版本使他依賴的 log
版本與 service2
的 log
版本一致,但可能會導致 service1
不能工作。
你可能說了,這又不行,那又不行,怎麼辦呢?別急,往下看,maven 解決依賴衝突主要用兩種方法:
- 排除低版本,直接用高版本
最理想的狀況就是直接排除低版本,依賴高版本,一般情況下高版本會兼容低版本。如果 service2
並沒有調用 log4j.1.2.9
升級所摒棄的方法或類時, 可以使用 <exclusion>
標籤,排除掉 service2
中的 log
。還是以上圖舉例:
<dependency> <groupId>com.nasus</groupId> <artifactId>service2</artifactId> <version>1.0.0</version> <!-- 排除低版本 log4j --> <exclusions> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency>
看著到這裡,你可能又說了。如果 service2
有用到 log
升級所摒棄的方法或類;而 service1
又必須用新版本的 log
,怎麼辦?
第一,一般情況下,第三方依賴不會出現這種情況。如果出現了,那你就到 maven 中央倉庫找下兼容兩個版本的依賴。如果找不到,那隻能換依賴。
第二,如果是自己公司的 jar 出現這種情況,那就是你們的 jar 管理非常混亂。建議重新開發,兼容舊版本。
四、使用 Maven Helper 插件解決依賴衝突
idea plugin 中搜索 maven helper
插件安裝完之後,打開 pom 文件,發現左下角有個 Depandency Analyzer
選項,點擊進入選 conflicts 選項,就可以看到當前有衝突的 jar 包,在右邊 exclude 掉紅色衝突的版本即可。

解決衝突
-END-