maven項目的pom.xml文件給項目導入了不一樣的jar包,有時候不一樣的dependency會引入同一個jar包的不一樣版本。spring
當不一樣版本的jar包被依賴後,可能會出現:apache
在pom.xml文件的目錄下使用mvn dependency:tree命令能夠查看jar包的傳遞依賴。api
使用-Dverbose 參數能夠列出更詳細的信息。tomcat
mvn -Dverbose dependency:treebash
從命令運行的輸出內容來看,該命令執行的時候會從新build一次。eclipse
若是節選輸出內容的其中一部分,多是這樣的:jsp
[INFO] +- org.apache.tomcat:tomcat-servlet-api:jar:7.0.70:compile [INFO] +- org.apache.tomcat:tomcat-jsp-api:jar:7.0.70:compile [INFO] | +- org.apache.tomcat:tomcat-el-api:jar:7.0.70:compile [INFO] | \- (org.apache.tomcat:tomcat-servlet-api:jar:7.0.70:compile - omitted for duplicate) [INFO] +- net.sf.jasperreports:jasperreports:jar:5.6.0:compile [INFO] | +- (commons-beanutils:commons-beanutils:jar:1.8.0:compile - omitted for conflict with 1.8.3) [INFO] | +- commons-collections:commons-collections:jar:3.2.1:compile [INFO] | +- commons-digester:commons-digester:jar:2.1:compile [INFO] | | +- (commons-beanutils:commons-beanutils:jar:1.8.3:compile - omitted for duplicate) [INFO] | | \- (commons-logging:commons-logging:jar:1.1.1:compile - omitted for duplicate)
遞歸依賴的關係列的算是比較清楚了,每行都是一個jar包,根據縮進能夠看到依賴的關係。maven
解決重複依賴和衝突的方法:ide
1,修改pom文件中兩個dependency元素的位置。若是兩個dependency都引用了一個jar包,可是版本不一樣,classloader只會加載jar包在pom文件中出現的第一個版本,之後出現的其餘版本的jar包會被忽略。工具
不建議使用該方法,由於引用不一樣版本的jar包自己就是很危險的。
2,使用<exclusions>標籤來去掉某個dependency依賴中的某一個jar包或一堆jar包,<exclusion>中的jar包或者依賴的相關jar包都會被忽略,從而在兩個dependency都依賴某個jar包時,能夠保證只使用其中的一個。
能夠這麼寫:
<dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.8.3.2</version> <exclusions> <exclusion> <artifactId>guava</artifactId> <groupId>com.google.guava</groupId> </exclusion> <exclusion> <artifactId>spring</artifactId> <groupId>org.springframework</groupId> </exclusion> </exclusions> </dependency>
對於Jar包衝突問題,咱們開發人員常常都會有碰到,當咱們使用一些jar包中的類、方法等,或者有時遇到一些日誌系統的問題(參考另外一篇文章Jar包衝突致使的日誌問題),咱們會遇到ClassNotFoundException,NoSuchFieldException,NoSuchMethodException 之類的運行時異常,從經驗上咱們就會判斷,Jar包衝突了。解決Jar包衝突問題,每一個人都有每一個人的方法,這裏我介紹一下個人方法,供你們參考。
當遇到jar包衝突時,咱們首先肯定是哪一個jar包衝突了,這個很容易,看咱們調用的類或方法,是屬於哪一個Jar包。而後就是要找出衝突了,我這裏使用命令
mvn dependency:tree -Dverbose -Dincludes=<groupId>:<artifactId>
填寫上Jar包的groupId和artifactId,能夠只有一個,可是中間的冒號不要少,這樣就會輸出依賴樹,並且是僅包含這個Jar包的依賴樹,這樣那些地方依賴了這個Jar包的那個版本就一目瞭然了。
例如,個人項目中notify-common包存在衝突,咱們使用命令
mvn dependency:tree -Dverbose -Dincludes=:notify-common
獲得依賴樹輸出
[INFO] com.taobao.wlb:bis-server:war:1.0-SNAPSHOT [INFO] +- com.taobao.wlb:bis-core:jar:1.0-SNAPSHOT:compile [INFO] | \- com.taobao.logistics:schedule-client:jar:1.1.1:compile [INFO] | \- (com.taobao.notify:notify-common:jar:1.8.15:compile - omitted for conflict with 1.8.19.26) [INFO] \- com.taobao.notify:notify-tr-client:jar:1.8.19.26:compile [INFO] +- com.taobao.notify:notify-common:jar:1.8.19.26:compile [INFO] \- com.taobao.notify:notify-remoting:jar:1.8.19.26:compile [INFO] \- (com.taobao.notify:notify-common:jar:1.8.19.26:compile - omitted for duplicate)
看一下依賴樹中全部的葉子節點就是全部的notify-common包,咱們能夠看到咱們依賴的bis-core中依賴了schedule-client包,它依賴了一個notify-common包,版本是1.8.15,第四行的後面也提示了這個包同其餘包有衝突- omitted for conflict with 1.8.19.26)
。而咱們的系統依賴的notify-tr-client包所依賴的版本是1.8.19.26,因而咱們知道是這裏衝突了,在POM排除掉依賴,OK了。
這裏咱們對咱們執行的命令作一個簡單的說明。
mvn dependency:tree -Dverbose -Dincludes=<groupId>:<artifactId>
第一部分mvn dependency:tree
是maven依賴的分析命令,做用是對咱們的項目的依賴進行分析,並輸出項目依賴樹
第二部分-Dverbose
的做用是添加了verbose一個環境變量,起的做用是在分析項目依賴時輸出明細,這樣項目中依賴的全部引用都會被輸出出來,包含了全部的間接引用,會有不少不少,咱們只須要咱們要找的,因此就須要第三個參數了
第三部分-Dincludes=<groupId>:<artifactId>
的做用就是進行過濾,只包含咱們想要的依賴的依賴時,排除掉其它不須要的,依賴樹的全部葉子節點就是咱們的找的依賴包。其中的groupId和artifactId能夠只填寫一個,爲了保證準確性,通常都會填兩個(填寫時不包括尖括號)。
其餘方法:
一、對於maven工程,個人辦法是使用eclipse來解決,點開pom.xml,切換到hierarchy dependency,右上角搜索對應的包,能夠清晰地看到衝突版本
二、可使用idea,在pom.xml中右單擊 選擇Diagrams-》show dependencies
三、mvn dependency:tree -Dverbose > tree.log
直接輸出衝突的jar文件
1. 依賴的配置
根元素project下的dependencies能夠包含一個或多個dependency元素,以聲明一個或多個依賴。每一個依賴能夠包含的元素有:
groupId、artifactId和version:依賴的基本座標,座標三元素。
type:依賴的類型,對應於項目定義的packaging,大部分狀況下不須要定義,使用默認值jar。
scope:依賴的範圍。
optional:標記依賴是否可選。
exclusions:用來排除傳遞性依賴。
依賴範圍(scope) | 對於編譯classpath有效 | 對於測試classpath有效 | 對於運行時classpath有效 |
編譯依賴範圍:compile | Y | Y | Y |
測試依賴範圍:test | - | Y | - |
已提供依賴範圍:provided | Y | Y | - |
運行時依賴範圍:runtime | - | Y | Y |
系統依賴範圍:system | Y | Y | - |
另外還有導入依賴範圍:import,該範圍不會對三種classpath產生實際的影響。
當A有一個compile範圍的B依賴,B有一個compile範圍的C依賴,那麼C就會成爲A的compile範圍依賴,C是A的一個傳遞性依賴。
有了傳遞性依賴的機制,在使用某個依賴時就不須要考慮它依賴了什麼,也不須要擔憂引入多餘的依賴。Maven會解析各個直接依賴的POM,將那些必要的間接依賴,以傳遞性依賴的形式引入到當前的項目中。
須要注意的是,可選依賴不會被傳遞。
compile | test | provided | runtime | |
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
當兩個依賴路徑上有兩個版本的依賴X時,有如下兩個依賴調解原則:
第一原則:路徑最近者優先;
第二原則:路徑長度同樣時,第一聲明者優先。
當項目A依賴於項目B,可是不想引入傳遞性依賴C,而是本身顯示的聲明對項目C另外一個版本的依賴,使用exclusions元素聲明排除性依賴。
exclusions能夠包含一個或者多個exclusion子元素,聲明exclusion時只須要groupId和artifactId,不須要version元素。
當項目中依賴了同一項目的不一樣模塊,它們的版本都是相同的,所以在升級的時候,這些依賴的版本會一塊兒升級。爲了不重複,且須要修改時只修改一處,能夠經過歸類依賴來解決。
使用properties元素定義Maven屬性,如springframework.version子元素,並定義其值。有了這個屬性定義,maven運行時會將POM中全部的${springframwork.version}替換成定義的實際值。
使用dependency:list和dependency:tree 幫助咱們詳細瞭解項目中全部依賴的具體信息。
使用dependency:analyze工具能夠幫助分析當前項目的依賴。
analyze的結果中包含了兩部分:
Used undeclared dependencies:項目中使用但未顯式聲明的依賴。這種依賴意味着潛在的風險;
Unused declared dependencise:項目中未使用的,但顯式聲明的依賴。對於這種依賴不能直接刪除,由於analyze只會分析編譯和測試須要的依賴,其餘依賴它沒法發現,所以須要仔細分析。