Maven中jar包沖突原理與解決辦法
Maven中jar包沖突是開發(fā)過程中比較常見而又令人頭疼的問題,我們需要知道 jar包沖突的原理,才能更好的去解決jar包沖突的問題。本文將從jar包沖突的原理和解決兩個(gè)方面闡述Maven中jar包沖突的解決辦法。
一、Maven中jar包沖突產(chǎn)生原因
MAVEN項(xiàng)目運(yùn)行中如果報(bào)如下錯(cuò)誤:
Caused by:java.lang.NoSuchMethodError
Caused by: java.lang.ClassNotFoundException
十有八九是Maven jar包沖突造成的。那么jar包沖突是如何產(chǎn)生的?
首先我們需要了解jar包依賴的傳遞性。
1、依賴傳遞
當(dāng)我們需要A的依賴的時(shí)候,就會(huì)在pom.xml中引入A的jar包;而引入的A的jar包中可能又依賴B的jar包,這樣Maven在解析pom.xml的時(shí)候,會(huì)依次將A、B 的jar包全部都引入進(jìn)來。
舉個(gè)例子:
在Spring Boot應(yīng)用中導(dǎo)入Hystrix和原生Guava的jar包:
<!--原生Guava API--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>20.0</version> </dependency> <!--hystrix依賴(包含對Guava的依賴)--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>1.4.4.RELEASE</version> </dependency>
利用Maven Helper插件得到項(xiàng)目導(dǎo)入的jar包依賴樹:

從圖中可以看出Hystrix包含對Guava jar包依賴的引用: Hystrix -> Guava,所以在引入Hystrix的依賴的時(shí)候,會(huì)將Guava的依賴也引入進(jìn)來。
2、jar包沖突原理
那么jar包是如何產(chǎn)生沖突的?
假設(shè)有如下依賴關(guān)系:
A->B->C->D1(log 15.0):A中包含對B的依賴,B中包含對C的依賴,C中包含對D1的依賴,假設(shè)是D1是日志jar包,version為15.0
E->F->D2(log 16.0):E中包含對F的依賴,F(xiàn)包含對D2的依賴,假設(shè)是D2是同一個(gè)日志jar包,version為16.0
當(dāng)pom.xml文件中引入A、E兩個(gè)依賴后,根據(jù)Maven傳遞依賴的原則,D1、D2都會(huì)被引入,而D1、D2是同一個(gè)依賴D的不同版本。
當(dāng)我們在調(diào)用D2中的method1()方法,而D1中是15.0版本(method1可能是D升級后增加的方法),可能沒有這個(gè)方法,這樣JVM在加載A中D1依賴的時(shí)候,找不到method1方法,就會(huì)報(bào)NoSuchMethodError的錯(cuò)誤,此時(shí)就產(chǎn)生了jar包沖突。
注:
如果在調(diào)用method2()方法的時(shí)候,D1、D2都含有這個(gè)方法(且升級的版本D2沒有改動(dòng)這個(gè)方法,這樣即使D有多個(gè)版本,也不會(huì)產(chǎn)生版本沖突的問題。)
舉個(gè)例子:

利用Maven Helper插件分析得出:Guava這個(gè)依賴包產(chǎn)生沖突。
我們之前導(dǎo)入了Guava的原生jar包,版本號是20.0;而現(xiàn)在提示Guava產(chǎn)生沖突,且沖突發(fā)生位置是Hystrix所在的jar包,所以可以猜測Hystrix中包含了對Guava不同版本的jar包的引用。
為了驗(yàn)證我們的猜想,使用Maven Helper插件打印出Hystrix依賴的jar tree:

可以看到:Hystrix jar中所依賴的Guava jar包是15.0版本的,而我們之前在pom.xml中引入的原生Guava jar包是20.0版本的,這樣Guava就有15.0 與20.0這兩個(gè)版本,因此發(fā)生了jar包沖突。
二、 Maven中jar包沖突的解決方案
Maven 解析 pom.xml 文件時(shí),同一個(gè) jar 包只會(huì)保留一個(gè),那么面對多個(gè)版本的jar包,需要怎么解決呢?
1、 Maven默認(rèn)處理策略最短路徑優(yōu)先
Maven 面對 D1 和 D2 時(shí),會(huì)默認(rèn)選擇最短路徑的那個(gè) jar 包,即 D2。E->F->D2 比 A->B->C->D1 路徑短 1。
最先聲明優(yōu)先
如果路徑一樣的話,如: A->B->C1, E->F->C2 ,兩個(gè)依賴路徑長度都是 2,那么就選擇最先聲明。
2、移除依賴:用于排除某項(xiàng)依賴的依賴jar包
(1)我們可以借助Maven Helper插件中的Dependency Analyzer分析沖突的jar包,然后在對應(yīng)標(biāo)紅版本的jar包上面點(diǎn)擊execlude,就可以將該jar包排除出去。

再刷新以后沖突就會(huì)消失。
(2)手動(dòng)排除
或者手動(dòng)在pom.xml中使用<exclusion>標(biāo)簽去排除沖突的jar包(上面利用插件Maven Helper中的execlude方法其實(shí)等同于該方法):
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>1.4.4.RELEASE</version> <exclusions> <exclusion> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </exclusion> </exclusions> </dependency>
mvn分析包沖突命令:
mvn dependency:tree
3 版本鎖定原則:一般用在繼承項(xiàng)目的父項(xiàng)目中
正常項(xiàng)目都是多模塊的項(xiàng)目,如moduleA和moduleB共同依賴X這個(gè)依賴的話,那么可以將X抽取出來,同時(shí)設(shè)置其版本號,這樣X依賴在升級的時(shí)候,不需要分別對moduleA和moduleB模塊中的依賴X進(jìn)行升級,避免太多地方(moduleC、moduleD…)引用X依賴的時(shí)候忘記升級造成jar包沖突,這也是實(shí)際項(xiàng)目開發(fā)中比較常見的方法。
首先定義一個(gè)父pom.xml,將公共依賴放在該pom.xml中進(jìn)行聲明:
<properties>
<spring.version>spring4.2.4</spring.version>
<properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.versio}</version>
</dependency>
</dependencies>
</dependencyManagement>
這樣如moduleA和moduleB在引用Spring-beans jar包的時(shí)候,直接使用父pom.xml中定義的公共依賴就可以:
moduleA在其pom.xml使用spring-bean的jar包(不用再定義版本):
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> </dependencies>
moduleB在其pom.xml使用spring-bean的jar包如上類似:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> </dependencies>
以上就是日常開發(fā)中解決Maven沖突的幾個(gè)小方案,當(dāng)然實(shí)際開發(fā)中jar包沖突的問題可能遠(yuǎn)遠(yuǎn)比這個(gè)更復(fù)雜,需要具體問題具體處理。更多相關(guān)Maven jar包沖突內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用screw來對比數(shù)據(jù)庫表和字段差異
這篇文章主要介紹了Java如何使用screw來對比數(shù)據(jù)庫表和字段差異,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-12-12
從JVM的內(nèi)存管理角度分析Java的GC垃圾回收機(jī)制
這篇文章主要介紹了從JVM的內(nèi)存管理角度分析Java的GC垃圾回收機(jī)制,帶有GC是Java語言的重要特性之一,需要的朋友可以參考下2015-11-11
SpringMVC實(shí)現(xiàn)controller中獲取session的實(shí)例代碼
本篇文章主要介紹了SpringMVC實(shí)現(xiàn)controller中獲取session的實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-02-02
XFire構(gòu)建web service客戶端的五種方式
本篇文章主要介紹了XFire構(gòu)建web service客戶端的五種方式。具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-01-01
Java?list如何實(shí)現(xiàn)將指定元素排在第一位
這篇文章主要為大家詳細(xì)介紹了Java?list中如何實(shí)現(xiàn)將指定元素排在第一位,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02
Spring使用aop切面編程時(shí)要給那些類加注解的實(shí)例
在使用切面編程時(shí),通常需要為以下類或組件添加注解來標(biāo)識(shí)它們,以便 Spring 或其他切面框架能夠正確識(shí)別和處理它們,這篇文章主要介紹了Spring使用aop切面編程時(shí)要給那些類加注解,需要的朋友可以參考下2023-11-11
教你使用idea搭建ssm詳細(xì)教程(Spring+Spring Mvc+Mybatis)
今天教大家使用idea搭建ssm詳細(xì)教程(Spring+Spring Mvc+Mybatis),文中有非常詳細(xì)的圖文介紹及代碼示例,對正在學(xué)習(xí)使用idea的小伙伴很有幫助,需要的朋友可以參考下2021-05-05

