Maven中jar包沖突是開發過程中比較常見而又令人頭疼的問題,我們需要知道 jar包沖突的原理,才能更好的去解決jar包沖突的問題。本文將從jar包沖突的原理和解決兩個方面闡述Maven中jar包沖突的解決辦法。
一、Maven中jar包沖突產生原因
MAVEN項目運行中如果報如下錯誤:
Caused by:java.lang.NoSuchMethodError
Caused by: java.lang.ClassNotFoundException
十有八九是Maven jar包沖突造成的。那么jar包沖突是如何產生的?
首先我們需要了解jar包依賴的傳遞性。
1、依賴傳遞
當我們需要A的依賴的時候,就會在pom.xml中引入A的jar包;而引入的A的jar包中可能又依賴B的jar包,這樣Maven在解析pom.xml的時候,會依次將A、B 的jar包全部都引入進來。
舉個例子:
在Spring Boot應用中導入Hystrix和原生Guava的jar包:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!--原生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插件得到項目導入的jar包依賴樹:
從圖中可以看出Hystrix包含對Guava jar包依賴的引用: Hystrix -> Guava,所以在引入Hystrix的依賴的時候,會將Guava的依賴也引入進來。
2、jar包沖突原理
那么jar包是如何產生沖突的?
假設有如下依賴關系:
A->B->C->D1(log 15.0):A中包含對B的依賴,B中包含對C的依賴,C中包含對D1的依賴,假設是D1是日志jar包,version為15.0
E->F->D2(log 16.0):E中包含對F的依賴,F包含對D2的依賴,假設是D2是同一個日志jar包,version為16.0
當pom.xml文件中引入A、E兩個依賴后,根據Maven傳遞依賴的原則,D1、D2都會被引入,而D1、D2是同一個依賴D的不同版本。
當我們在調用D2中的method1()方法,而D1中是15.0版本(method1可能是D升級后增加的方法),可能沒有這個方法,這樣JVM在加載A中D1依賴的時候,找不到method1方法,就會報NoSuchMethodError
的錯誤,此時就產生了jar包沖突。
注:
如果在調用method2()方法的時候,D1、D2都含有這個方法(且升級的版本D2沒有改動這個方法,這樣即使D有多個版本,也不會產生版本沖突的問題。)
舉個例子:
利用Maven Helper插件分析得出:Guava這個依賴包產生沖突。
我們之前導入了Guava的原生jar包,版本號是20.0;而現在提示Guava產生沖突,且沖突發生位置是Hystrix所在的jar包,所以可以猜測Hystrix中包含了對Guava不同版本的jar包的引用。
為了驗證我們的猜想,使用Maven Helper插件打印出Hystrix依賴的jar tree:
可以看到:Hystrix jar中所依賴的Guava jar包是15.0版本的,而我們之前在pom.xml中引入的原生Guava jar包是20.0版本的,這樣Guava就有15.0 與20.0這兩個版本,因此發生了jar包沖突。
二、 Maven中jar包沖突的解決方案
Maven 解析 pom.xml 文件時,同一個 jar 包只會保留一個,那么面對多個版本的jar包,需要怎么解決呢?
1、 Maven默認處理策略最短路徑優先
Maven 面對 D1 和 D2 時,會默認選擇最短路徑的那個 jar 包,即 D2。E->F->D2 比 A->B->C->D1 路徑短 1。
最先聲明優先
如果路徑一樣的話,如: A->B->C1, E->F->C2 ,兩個依賴路徑長度都是 2,那么就選擇最先聲明。
2、移除依賴:用于排除某項依賴的依賴jar包
(1)我們可以借助Maven Helper插件中的Dependency Analyzer分析沖突的jar包,然后在對應標紅版本的jar包上面點擊execlude,就可以將該jar包排除出去。
再刷新以后沖突就會消失。
(2)手動排除
或者手動在pom.xml中使用<exclusion>
標簽去排除沖突的jar包(上面利用插件Maven Helper中的execlude方法其實等同于該方法):
1
2
3
4
5
6
7
8
9
10
11
|
< 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分析包沖突命令:
1
|
mvn dependency:tree |
3 版本鎖定原則:一般用在繼承項目的父項目中
正常項目都是多模塊的項目,如moduleA和moduleB共同依賴X這個依賴的話,那么可以將X抽取出來,同時設置其版本號,這樣X依賴在升級的時候,不需要分別對moduleA和moduleB模塊中的依賴X進行升級,避免太多地方(moduleC、moduleD…)引用X依賴的時候忘記升級造成jar包沖突,這也是實際項目開發中比較常見的方法。
首先定義一個父pom.xml,將公共依賴放在該pom.xml中進行聲明:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
< 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包的時候,直接使用父pom.xml中定義的公共依賴就可以:
moduleA在其pom.xml使用spring-bean的jar包(不用再定義版本):
1
2
3
4
5
6
|
< dependencies > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-beans</ artifactId > </ dependency > </ dependencies > |
moduleB在其pom.xml使用spring-bean的jar包如上類似:
1
2
3
4
5
6
|
< dependencies > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-beans</ artifactId > </ dependency > </ dependencies > |
以上就是日常開發中解決Maven沖突的幾個小方案,當然實際開發中jar包沖突的問題可能遠遠比這個更復雜,需要具體問題具體處理。更多相關Maven jar包沖突內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/noaman_wgs/article/details/81137893