《Maven官方文檔》-Maven依賴機製簡介(一)
依賴機製是Maven最為用戶熟知的特性之一,同時也是Maven所擅長的領域之一。單個項目的依賴管理並不難,
但是當你麵對包含數百個模塊的多模塊項目和應用時,Maven能幫你保證項目的高度控製力和穩定性。
大綱:
- 傳遞性依賴
- 排除、可選依賴
- 依賴範圍
- 依賴管理
- 導入依賴
- 係統依賴
傳遞性依賴
傳遞性依賴是Maven2.0的新特性。假設你的項目依賴於一個庫,而這個庫又依賴於其他庫。你不必自己去找出所有這些依賴,你隻需要加上你直接依賴的庫,Maven會隱式的把這些庫間接依賴的庫也加入到你的項目中。這個特性是靠解析從遠程倉庫中獲取的依賴庫的項目文件實現的。一般的,這些項目的所有依賴都會加入到項目中,或者從父項目繼承,或者通過傳遞性依賴。
傳遞性依賴的嵌套深度沒有任何限製,隻是在出現循環依賴時會報錯。
傳遞性依賴會導致包含庫的依賴圖增長的非常大。為了解決這個問題,Maven也提供了額外的機製,能讓你指定哪些依賴會被包含:
- 依賴調解 – 當項目中出現多個版本構件依賴的情形,依賴調解決定最終應該使用哪個版本。目前,Maven 2.0隻支持“短路徑優先”原則,意思是項目會選擇依賴關係樹中路徑最短的版本作為依賴。當然,你也可以在項目POM文件中顯式指定使用哪個版本。值得注意的是,在Maven2.0.8及之前的版本中,當兩個版本的依賴路徑長度一致時,哪個依賴會被使用是不確定的。不過從Maven 2.0.9開始,POM中依賴聲明的順序決定了哪個版本會被使用,也叫作”第一聲明原則”。
- “短路徑優先”意味著項目依賴關係樹中路徑最短的版本會被使用。例如,假設A、B、C之間的依賴關係是A->B->C->D(2.0)和A->E->(D1.0),那麼D(1.0)會被使用,因為A通過E到D的路徑更短。但如果你想要強製使用D(2.0),那你也可以在A中顯式聲明對D(2.0)的依賴。
- 依賴管理 – 在出現傳遞性依賴或者沒有指定版本時,項目作者可以通過依賴管理直接指定模塊版本。之前的章節說過,由於傳遞性依賴,盡管某個依賴沒有被A直接指定,但也會被引入。相反的,A也可以將D加入<dependencyManagement>元素中,並在D可能被引用時決定D的版本號。
- 依賴範圍 – 你可以指定隻在當前編譯範圍內包含合適的依賴。 下麵會介紹更多相關的細節。
- 排除依賴 – 如果項目X依賴於項目Y,項目Y又依賴項目Z,項目X的所有者可以使用”exclusion”元素來顯式排除項目Z。
- 可選依賴 – 如果項目Y依賴項目Z,項目Y的所有者可以使用”optional”元素來指定項目Z作為X的可選依賴。那麼當項目X依賴項目Y時,X隻依賴Y並不依賴Y的可選依賴Z。項目X的所有者也可以根據自己的意願顯式指定X對Z的依賴。(你可以把可選依賴理解為默認排除)。
依賴範圍
依賴範圍會影響傳遞性依賴,同時也會影響項目構建任務中使用的classpath。
Maven有以下6種依賴範圍:
- compile
這是默認範圍。如果沒有指定,就會使用該依賴範圍。編譯依賴對項目所有的classpath都可用。此外,編譯依賴會傳遞到依賴的項目。 - provided
和compile範圍很類似,但provided範圍表明你希望由JDK或者某個容器提供運行時依賴。例如,當使用Java EE構建一個web應用時,你會設置對Servlet API和相關的Java EE APIs的依賴範圍為provided,因為web容器提供了運行時的依賴。provided依賴隻對編譯和測試classpath有效,並且不能傳遞。 - runtime
runtime範圍表明編譯時不需要依賴,而隻在運行時依賴。此依賴範圍對運行和測試classpath有效,對編譯classpath無效。 - test
test範圍表明使用此依賴範圍的依賴,隻在編譯測試代碼和運行測試的時候需要,應用的正常運行不需要此類依賴。 - system
係統範圍與provided類似,不過你必須顯式指定一個本地係統路徑的JAR,此類依賴應該一直有效,Maven也不會去倉庫中尋找它。 - import(Maven2.0.9及以上)
import範圍隻適用於pom文件中的<dependencyManagement>部分。表明指定的POM必須使用<dependencyManagement>部分的依賴。因為依賴已經被替換,所以使用import範圍的依賴並不影響依賴傳遞。
每類依賴範圍(除了import)通過不同方式影響傳遞性依賴,具體如下表所示。最左側一列代表了直接依賴範圍,最頂層一行代表了傳遞性依賴的範圍,行與列的交叉單元格就表示最終的傳遞性依賴範圍。表中的“-“表示該傳遞性依賴將會被忽略。
compile | provided | runtime | test | |
compile | compile(*) | – | runtime | – |
provided | provided | – | provided | – |
runtime | runtime | – | runtime | – |
test | test | – | test | – |
(*)注意:這裏本來應該是compile範圍,那樣的話compile範圍都必須顯式指定-然而,有這樣一種情況,你依賴的、繼承自其它庫中的類的庫必須在編譯時可用。考慮到這個原因,即使在依賴性傳遞情況下,編譯時依賴仍然是compile範圍。
依賴管理
Maven提供了一個機製來集中管理依賴信息,叫做依賴管理元素”<dependencyManagement>”。假設你有許多項目繼承自同一個公有的父項目,那可以把所有依賴信息放在一個公共的POM文件,並且在子POM中簡單第引用該構件即可。通過一些例子可以更好的解釋這個機製。下麵是兩個繼承自同一個父項目的POM:
項目A
<project>
…
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
項目B
<project>
…
<dependencies>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
這兩個POM都依賴於同一個模塊,同時每個POM又各自依賴於一個無關的模塊。父項目的POM詳細信息如下所示:
<project>
…
<dependencyManagement>
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version><exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions></dependency>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency><dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
這樣兩個子項目的POM文件就簡單多了。
<project>
…
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
</dependency><dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<!– This is not a jar dependency, so we must specify type. –>
<type>bar</type>
</dependency>
</dependencies>
</project><project>
…
<dependencies>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<!– This is not a jar dependency, so we must specify type. –>
<type>war</type>
</dependency><dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<!– This is not a jar dependency, so we must specify type. –>
<type>bar</type>
</dependency>
</dependencies>
</project>
注意:在這兩個POM文件的依賴中,我們必須指定<type/>元素。因為與依賴管理元素匹配的依賴引用最小信息集是{groupId, artifactId, type, classfier}。許多情況下,依賴指向的jar不需要指定classfier。因為默認type是jar,默認classfiler為空,所以我們可以把信息集設置為{groupId, artifactId}。
依賴管理元素第二個非常有用的功能是控製傳遞性依賴中構件的版本。例子如下
項目A:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>A</artifactId>
<packaging>pom</packaging>
<name>A</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>c</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>d</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
項目B:
<project>
<parent>
<artifactId>A</artifactId>
<groupId>maven</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>B</artifactId>
<packaging>pom</packaging>
<name>B</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>d</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>c</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
當在maven中有項目依賴B時,不管它們的pom文件中指定的版本是什麼,構件a,b,c和d的版本都是1.0。
- a和c都被聲明為這個項目的依賴,根據依賴調解,a和c的版本都是1.0.同時a和c的依賴範圍都被顯式指定為runtime。
- b定義在B的父項目的<dependencyManagement>元素中,因為在依賴性傳遞中<dependencyManagement>優先於依賴調解,所以b的版本是1.0,b是編譯依賴範圍。
- 最後,d是定義在B的<dependencyManagement>元素中。
依賴管理的標簽詳細描述信息可以從這裏獲取項目描述符引用
導入依賴
這個章節描述的特性隻在Maven2.0.9及之後的版本才有。這意味著更早版本的Maven不會解析包含import元素的pom文件。因此在使用該特性前,你必須慎重考慮。如果你打算使用這個特性,我們建議你使用enforcer插件來強製使用Maven2.0.9及以上版本。
前麵的例子描述了怎麼通過繼承來指定管理的依賴。然而,這對於更大的項目通常會更複雜,因為一個項目隻能繼承自一個父項目。為了解決這個問題,項目可以導入其他項目的管理依賴,這可以通過聲明依賴一個包含值為”import”的<scope>元素的構件來實現。
最後更新:2017-05-19 16:02:22