Maven 基礎(二) | 解決依賴衝突的正確姿勢

  • 2020 年 2 月 18 日
  • 筆記

一、依賴原則

假設,在 JavaMavenService2 模組中,log4j 的版本是 1.2.7,在 JavaMavenService1 模組中,它雖然繼承於 JavaMavenService2 模組,但是它排除了在 JavaMavenService2 模組中繼承 1.2.7 的版本,自己引入了 1.2.9log4j 版本。

此時,相對於 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 項目依賴 Sercive1Service2,它倆是同一個路徑,那麼誰在 WebMavenDemopom.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。這時,如果依賴 log4jservice1service2 中的版本不一致就可能導致依賴衝突。如下圖:

依賴衝突的原因

注意,上面我用的是可能,並不是說滿足上面的條件就一定會發生依賴衝突。因為 maven 遵循上面提到的兩個原則:

  • 先定義先使用原則 (路徑層級相同情況下)
  • 路徑優先原則 (誰路徑短用誰)

2.1 依賴衝突會報什麼錯?

依賴衝突通常兩個錯:NoClassDefFoundErrorNoSuchMethodError,逐一講解下導致這兩種錯誤的原因:

  • 以上圖依賴關係為例,假設 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 版本與 service1log 版本一致,但它可能會導致 service2 不能工作。

2、如果 service2 是箇舊項目,找遍了也沒找到與 service1 版本一致的 log,這時可以嘗試拉低 service1 的版本使他依賴的 log 版本與 service2log 版本一致,但可能會導致 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-