2013-10-29 27 views
36

構建具有許多依賴關係的maven項目時,其中一些依賴項依賴於相同的庫,但使用運行應用程序時導致錯誤的其他版本。例如,如果我添加兩個不同的項目依賴關係,A和B都依賴於apache commons http client,但每個依賴於不同的版本,一旦類加載器加載A的apache commons http客戶端類,B將嘗試使用它們,因爲它們已經被類加載器加載了。java maven項目中衝突的庫版本

但是B的字節碼依賴於加載類的不同版本,在運行應用程序時會導致多個問題。常見的是methodnotfound異常(因爲A的http客戶端版本不再使用特定的方法)。

什麼是建設以避免這種衝突時的一般策略?是否必須手動檢查依賴關係樹以找出哪些公共庫互相混淆?

回答

22

歡迎來到maven dependency hell,因爲它很有名。隨着項目的增加和引入更多外部依賴關係,這是一個比較常見的問題。

除了Apache Commons(在你原來的問題中提到過),日誌框架(log4j,slf4j)是另一個常見的罪魁禍首。

我同意「matts」給出的有關如何在識別後解決衝突的建議。在早期捕獲這些版本衝突方面,您也可以使用Maven的「執行者」插件。請參閱"dependencyConvergence" config。另見this SO post

使用執行者插件會在版本衝突時立即失敗,從而避免了手動檢查。這是一種積極的策略,但可以防止引發問題/發佈的運行時問題類型。像任何事情一樣,執行者插件有優點和缺點。我們在去年開始使用它,但後來發現它可能是一種祝福和詛咒。 libs/frameworks的許多版本都是向後兼容的,所以在編譯時和運行時,版本1.2.3和1.2.4的版本(直接或間接)都是很好的。但是,執行者插件將標記此衝突並要求您聲明所需的版本。假設依賴衝突的數量很少,這並不需要太多的工作。但是,一旦你引入了一個大型框架(例如Spring MVC),它就會變得討厭。

希望這是有用的信息。

33

您可以使用Maven依賴項插件的tree goal來顯示項目中的所有傳遞依賴項,並查找說明「因衝突而忽略」的依賴項。 1

mvn dependency:tree -Dverbose 
mvn dependency:tree -Dverbose | grep 'omitted for conflict' 

一旦你知道它的依賴性有版本衝突,你可以使用includes參數只顯示依賴關係導致一個看一個特定的依賴關係是如何被拉入。例如,一個項目中的不同的C版本由A和B拉到:

mvn dependency:tree -Dverbose -Dincludes=project-c 

[INFO] com.my-company:my-project:jar:1.0-SNAPSHOT 
[INFO] +- project-a:project-a:jar:0.1:compile 
[INFO] | \- project-c:project-c:jar:1.0:compile 
[INFO] \- project-b:project-b:jar:0.2:compile 
[INFO] \- project-x:project-x:jar:0.1:compile 
[INFO]  \- (project-c:project-c:jar:2.0:compile - omitted for conflict) 

要真正解決衝突,在某些情況下有可能找到一個版本的傳遞依賴,無論你的主要依賴將與工作的。將傳遞依賴添加到您的pom的dependencyManagement部分,然後嘗試更改版本,直到有效。

但是,在其他情況下,可能無法找到適用於所有人的依賴版本。在這些情況下,您可能必須退回其中一個主要依賴項的版本,才能使用適用於所有人的傳遞依賴項版本。例如,在上面的例子中,A 0.1使用C 1.0,B 0.2使用C 2.0。假設C 1.0和2.0完全不兼容。但也許你的項目可能會使用B 0.1,而這恰好取決於與C 1.0兼容的C 1.5。

當然,這兩種策略並不總是有效,但我之前發現他們取得了成功。其他更激進的選項包括打包您自己的依賴項版本,以修復不兼容性或試圖在單獨的類加載器中隔離這兩個依賴項。

+0

,因爲它似乎我的情況是,不能滿足每個人的依賴。這主要是因爲我將遺留軟件(可惜遺漏給我的遺留軟件)與具有共同依賴關係的較新庫合併在一起。因此,總而言之,最好的解決方案是在編譯時或之前收到錯誤和警告,以表明我需要做一些手動工作。至少它爲我節省了部署和解決衝突的麻煩。執行者插件似乎對這個方向很好。 –

+2

由matts完美答案。 對我來說,我留下了一個問題,爲什麼不同版本的多個罐子不能一起存在?爲什麼模塊a鏈接到0.1版本的X can not使用鏈接到0.2版本的模塊b? 答案是 - >因爲類名: 「每個加載到虛擬機中的類都由三件事來唯一標識,它們的名稱,包和它的類加載器。 from: https://kepler-project.org/developers/teams/framework/design-docs/trade-studies/custom-class-loading/using-multiple-classloaders。 – Robocide

1

我想託德和馬茨答案的事實擴展,你可以:

  • mvn dependency:tree -Dverbose -Dincludes=project-c

  • 添加<exclusions/>標籤爲您所有的依賴具有的project-c傳遞依賴。

  • 或者,或者,在您的項目中,明確定義project-c作爲依賴項以覆蓋可傳遞的並避免衝突。 (使用`-Dverbose時,這仍然會顯示在你的樹中)。

或者,如果這些項目在您的控制之下,您可以簡單地升級版本project-c

1

你可以在你的pom中使用maven-enforcer插件來強制傳遞依賴的特定版本。這可以幫助您在發生衝突時防止pom配置的遺漏。

這是對我有用的,我能夠改變版本以匹配。如果您無法更改版本,那麼這不會很有幫助。

Dependency Convergence

<project> 
... 
    <build> 
    <plugins> 
     ... 
     <plugin> 
     <groupId>org.apache.maven.plugins</groupId> 
     <artifactId>maven-enforcer-plugin</artifactId> 
     <version>1.4</version> 
     <executions> 
      <execution> 
      <id>enforce</id> 
      <configuration> 
       <rules> 
       <dependencyConvergence/> 
       </rules> 
      </configuration> 
      <goals> 
       <goal>enforce</goal> 
      </goals> 
      </execution> 
     </executions> 
     </plugin> 
     ... 
    </plugins> 
    </build> 
    ... 
</project> 

使用括號的依賴強制版本:

<dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>slf4j-api</artifactId> 
     <scope>compile</scope> 
     <version>[1.0.0]</version> 
</dependency>