Maven的依賴可以真的很難解決沖突。 這篇文章的目的是讓讀者更好地理解什么是版本沖突,為什么最好避免它們。 我將開始一個(gè)短篇故事,大多數(shù)讀者可能涉及到。
這個(gè)故事
首先,想象你已經(jīng)開始致力于一個(gè)大型和有趣的項(xiàng)目,它使用許多不同的技術(shù)庫,使你的生活更容易為工程師。 作為一個(gè)額外的好處,Maven管理圖書館下載的庫版本的選擇; 所以他們可以很容易地更新。 開發(fā)的項(xiàng)目繼續(xù)下去,直到有一天你遇到一個(gè)庫,減少了驗(yàn)證的復(fù)雜性顯著發(fā)展; 然后決定包括在您的項(xiàng)目。
但突然間,你得到一個(gè)錯(cuò)誤的2223行代碼,這是一條不同的你。 那些浮現(xiàn)在你腦海的第一件事是,“多么奇怪! 這行代碼沒有失敗; 它必須是一個(gè)錯(cuò)誤在我的代碼; 我必須創(chuàng)建/配置不正確”。 然而,你意識(shí)到的代碼沒有任何問題。 更徹底的調(diào)試過程“可能”顯示,有一個(gè)NullPointerException或NoSuchMethodException庫類,您不知道存在。 第一反應(yīng)是更新新版本的庫依賴關(guān)系; 但這并不能解決問題。 接下來你要做的就是看看圖書館類本身的代碼; 發(fā)現(xiàn)該方法/類不存在; 即使更新新庫添加到您的項(xiàng)目。
此外,事情變得更加不可思議當(dāng)你決定訪問本地Maven存儲(chǔ)庫.m2,你發(fā)現(xiàn)有兩個(gè)不同版本的庫。 您的項(xiàng)目使用的是舊版本,不知道為什么…
恭喜你! 你剛剛和版本庫沖突發(fā)現(xiàn)撞您的項(xiàng)目。
請(qǐng)注意:你把它當(dāng)你添加身份驗(yàn)證庫的故事。
它為什么會(huì)發(fā)生?
由于依賴關(guān)系可以與其他依賴不同的版本,可以產(chǎn)生沖突。 在此基礎(chǔ)上,我們可以畫一個(gè)依賴樹為我們的項(xiàng)目計(jì)劃X解釋:

從上面的樹方案中,很明顯,我們的項(xiàng)目X將使用的所有庫(Y,Z,H)即使在POM我們沒有顯式地指定它們。
實(shí)際上,在這種情況下,圖書館Z將導(dǎo)入到你的項(xiàng)目作為一個(gè)Maven依賴庫,即使你不知道圖書館的存在。 這種依賴是更好的被稱為過渡依賴。
自Y和G取決于不同版本的Z,我們已經(jīng)創(chuàng)建了一個(gè)庫版本沖突。 這個(gè)項(xiàng)目可以使用只有一個(gè)版本的Z在運(yùn)行時(shí)庫(1.0或2.0); 但不能兩者兼得。 如果我們使用一個(gè)不兼容的版本庫與另一個(gè)庫; 項(xiàng)目最終可能會(huì)產(chǎn)生錯(cuò)誤和崩潰。 讓我們假設(shè)庫Z在我們的項(xiàng)目錯(cuò)誤背后的罪魁禍?zhǔn)? 我們想知道哪個(gè)版本的Z正在創(chuàng)建的錯(cuò)誤。
我們使用Z版本?
我們的項(xiàng)目X使用Maven稱為默認(rèn)機(jī)制依賴機(jī)制解決,知道使用哪個(gè)庫的依賴關(guān)系。
讓我們看看依賴機(jī)制是如何工作的。
首先,圖書館的版本的節(jié)點(diǎn)是根(最近的項(xiàng)目X)將使用依賴關(guān)系樹。 然而,如果有幾個(gè)版本的相同的庫的節(jié)點(diǎn)都在相同的水平在樹上? 在這種情況下,發(fā)現(xiàn)第一個(gè)庫版本使用。 這意味著庫版本的選擇取決于依賴POM文件里面了,那些依賴宣布第一將選擇。
如何解決沖突
有兩種方法可以解決上述沖突。 第一和最簡(jiǎn)單的解決方案是導(dǎo)入庫G在圖書館Y內(nèi)部X的POM文件; 正如我上面解釋道。 然而,一個(gè)簡(jiǎn)潔的解決方案是進(jìn)口的最后版本Z(2.0)的直接依賴關(guān)系X; 內(nèi)部XPOM文件。 有一些運(yùn)氣,后者的解決方案如果圖書館工作Z支持向后兼容(如圖書館Y使用Z的v1.0)。 在這種情況下測(cè)試需要增加可靠性
重要提示:如果有版本沖突,它并不總是意味著你的項(xiàng)目將會(huì)崩潰,通常會(huì)有版本沖突,當(dāng)你使用大量的庫,但我們必須注意,不會(huì)崩潰,我們的項(xiàng)目使用的庫版本。
我怎么能更快地發(fā)現(xiàn)沖突?
提出的問題在上面的故事非常簡(jiǎn)單,我們很幸運(yùn),快速解決它。 然而,有時(shí)候,它不是一項(xiàng)容易的任務(wù)找到依賴沖突; 即使你已經(jīng)知道,這個(gè)錯(cuò)誤不是來自你的代碼邏輯。
不是很好有一個(gè)工具,測(cè)試庫項(xiàng)目中沖突嗎? 你很幸運(yùn)!
執(zhí)行者- Maven的愛的鐵拳
就是干這個(gè)的。 執(zhí)行者可以幫助開發(fā)者解決依賴沖突Maven POM文件中聲明通過分析所有庫。
這個(gè)插件使用很多不同的規(guī)則,但是我們只是感興趣:
dependencyConvergence
-確保所有依賴項(xiàng)收斂于相同的版本。
讓我們配置插件來使用規(guī)則寫在pom.xml:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<configuration>
<rules><dependencyConvergence/></rules>
</configuration>
</plugin>
</plugins>
從上面的配置,您可以執(zhí)行的目標(biāo)執(zhí)行者:執(zhí)行通過項(xiàng)目的命令行(mvn執(zhí)行者:執(zhí)行); 或者通過綁定一個(gè)Maven目標(biāo)階段。 這是非常有用的找到任何依賴沖突,項(xiàng)目可能會(huì)崩潰。 一旦執(zhí)行,插件返回樹列表顯示所有沖突(如果有的話)在項(xiàng)目:
Dependency convergence error for log4j:log4j:1.2.17 paths to dependency are:
+-com.ricston.conflict:conflict-info:2.1.3-SNAPSHOT
+-org.slf4j:slf4j-log4j12:1.7.6
+-log4j:log4j:1.2.17
and
+-com.ricston.conflict:conflict-info:2.1.3-SNAPSHOT
+-log4j:log4j:1.2.16
執(zhí)行者依賴樹寫道,正如上面你可以看到的,根源在哪里我們的項(xiàng)目“conflict-info”。
在這種情況下,圖書館log4j版本沖突。 我們有兩個(gè)圖書館log4j版本(1.2.17和1.2.16)。
log4j版本1.2.16是依靠由項(xiàng)目“conflict-info”; 雖然版本1.2.17取決于log4j的“slf4j-log4j12”。 在編譯和運(yùn)行時(shí),將使用log4j版本1.2.16,因?yàn)樗母?jié)點(diǎn)最近的我們的項(xiàng)目依賴關(guān)系樹。 因?yàn)橹挥幸粋€(gè)庫版本可以在運(yùn)行時(shí)使用,同一版本將用于“slf4j-log4j12”。
請(qǐng)注意,并不是所有項(xiàng)目依賴版本沖突會(huì)崩潰。 Maven的依賴機(jī)制負(fù)責(zé)選擇庫版本,在前面的部分。
最后,為了有更好的機(jī)會(huì)避免依賴版本沖突; 提取的依賴(和首選的版本)作為項(xiàng)目的直接的孩子。 這將確保所選的版本是用于所有子庫的依賴關(guān)系; 提供選擇的版本不崩潰。
結(jié)論
這一結(jié)論提供進(jìn)一步信息版本沖突,如何克服他們?cè)谀捻?xiàng)目中。
還有許多其他的方法來解決這種沖突; 甚至還有圖書數(shù)百頁使用不同的Maven配置說明解決方案。 本文描述了只有一個(gè)解決這些問題的方法。