《Maven官方文檔》-Maven依賴機制簡介

《Maven官方文檔》-Maven依賴機制簡介

原文地址 譯者:Tyrianhtml

依賴機制是Maven最爲用戶熟知的特性之一,同時也是Maven所擅長的領域之一。單個項目的依賴管理並不難,
可是當你面對包含數百個模塊的多模塊項目和應用時,Maven能幫你保證項目的高度控制力和穩定性。java

大綱:web

  •  傳遞性依賴
    • 排除、可選依賴
  • 依賴範圍
  • 依賴管理
    • 導入依賴
  • 系統依賴

 

傳遞性依賴

傳遞性依賴是Maven2.0的新特性。假設你的項目依賴於一個庫,而這個庫又依賴於其餘庫。你沒必要本身去找出全部這些依賴,你只須要加上你直接依賴的庫,Maven會隱式的把這些庫間接依賴的庫也加入到你的項目中。這個特性是靠解析從遠程倉庫中獲取的依賴庫的項目文件實現的。通常的,這些項目的全部依賴都會加入到項目中,或者從父項目繼承,或者經過傳遞性依賴。
傳遞性依賴的嵌套深度沒有任何限制,只是在出現循環依賴時會報錯。
傳遞性依賴會致使包含庫的依賴圖增加的很是大。爲了解決這個問題,Maven也提供了額外的機制,能讓你指定哪些依賴會被包含:redis

  • 依賴調解 – 當項目中出現多個版本構件依賴的情形,依賴調解決定最終應該使用哪一個版本。目前,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種依賴範圍:spring

  • 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)經過不一樣方式影響傳遞性依賴,具體以下表所示。最左側一列表明瞭直接依賴範圍,最頂層一行表明了傳遞性依賴的範圍,行與列的交叉單元格就表示最終的傳遞性依賴範圍。表中的「-「表示該傳遞性依賴將會被忽略。sql

  compile provided runtime test
compile compile(*) runtime
provided provided provided
runtime runtime runtime
test test test

(*)注意這裏原本應該是compile範圍,那樣的話compile範圍都必須顯式指定-然而,有這樣一種狀況,你依賴的、繼承自其它庫中的類的庫必須在編譯時可用。考慮到這個緣由,即便在依賴性傳遞狀況下,編譯時依賴仍然是compile範圍。mongodb

 

依賴管理

Maven提供了一個機制來集中管理依賴信息,叫作依賴管理元素」<dependencyManagement>」。假設你有許多項目繼承自同一個公有的父項目,那能夠把全部依賴信息放在一個公共的POM文件,而且在子POM中簡單第引用該構件便可。經過一些例子能夠更好的解釋這個機制。下面是兩個繼承自同一個父項目的POM:apache

項目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>api

項目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>元素的構件來實現。

項目B:

<project>
<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>maven</groupId>
<artifactId>A</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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>

假設A就是上一個例子中定義的POM,那麼最終的結果也是一致的。除了在B中定義的d模塊,全部A的管理依賴都會導入到B中。

項目X:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>X</artifactId>
<packaging>pom</packaging>
<name>X</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

項目Y:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>Y</artifactId>
<packaging>pom</packaging>
<name>Y</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>c</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

項目Z:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>Z</artifactId>
<packaging>pom</packaging>
<name>Z</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>maven</groupId>
<artifactId>X</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>maven</groupId>
<artifactId>Y</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

在上面的例子中,Z導入了X和Y的管理依賴。不過有個問題,X和Y都包含了依賴a。在這裏,會使用1.1版本的a,由於X先被聲明,而且a沒有在Z的依賴管理中聲明。

這個過程是遞歸進行的。假如X導入了另外的POM,Q,那麼當解析Z的時候,全部Q的管理依賴看上去就都像在X中定義的同樣。

當定義一個用於構建多項目的包含一些相關構件的依賴「庫」時,導入依賴就十分有效。從「庫」中引用一個或多個構件到項目中,是一種很常見的作法。然而,
保持項目中使用的依賴版本與庫中發佈的版本一致會有點麻煩。下面的模式描述了怎麼生成一個供其它項目使用的「物料清單」(BOM)。

項目的根元素是BOM pom文件。它定義了庫中建立的全部構件版本。其它要使用該庫的項目必須將該pom導入到其pom文件中的<dependencyManagement>元素中。

<project xmlns=」http://maven.apache.org/POM/4.0.0″ xmlns:xsi=」http://www.w3.org/2001/XMLSchema-instance」
xsi:schemaLocation=」http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd」>
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<project1Version>1.0.0</project1Version>
<project2Version>1.0.0</project2Version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
<version>${project1Version}</version>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
<version>${project1Version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>parent</module>
</modules>
</project>

parent子項目將BOM pom做爲它的父項目。這是一個簡單的多項目pom。

<project xmlns=」http://maven.apache.org/POM/4.0.0″ xmlns:xsi=」http://www.w3.org/2001/XMLSchema-instance」
xsi:schemaLocation=」http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd」>
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<project1Version>1.0.0</project1Version>
<project2Version>1.0.0</project2Version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
<version>${project1Version}</version>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
<version>${project1Version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>parent</module>
</modules>
</project>

接下來是真正的pom文件。

<project xmlns=」http://maven.apache.org/POM/4.0.0″ xmlns:xsi=」http://www.w3.org/2001/XMLSchema-instance」
xsi:schemaLocation=」http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd」>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.test</groupId>
<version>1.0.0</version>
<artifactId>parent</artifactId>
</parent>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
<version>${project1Version}</version>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
</project>

<project xmlns=」http://maven.apache.org/POM/4.0.0″ xmlns:xsi=」http://www.w3.org/2001/XMLSchema-instance」
xsi:schemaLocation=」http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd」>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.test</groupId>
<version>1.0.0</version>
<artifactId>parent</artifactId>
</parent>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
<version>${project2Version}</version>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
</dependencies>
</project>

下面的例子說明了怎麼在項目中使用「庫」,而沒必要指定依賴模塊的版本。

<project xmlns=」http://maven.apache.org/POM/4.0.0″ xmlns:xsi=」http://www.w3.org/2001/XMLSchema-instance」
xsi:schemaLocation=」http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd」>
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>use</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
</dependency>
</dependencies>
</project>

最後,當建立引入依賴的項目時,須要注意如下幾點:

  • 不要嘗試引入在當前pom中定義的子模塊pom。那會致使不能定位pom和編譯失敗。
  •  毫不要聲明導入其餘pom做爲目標pom的父項目(或者祖父項目等)的pom文件。這會致使循環解析,並觸發異常。
  •  當引用有傳遞性依賴的模塊時,須要指定依賴模塊的版本。不這樣作,這些模塊可能沒有肯定的版本,從而致使編譯失敗。(這在任何狀況下都應該是一個最佳實踐,由於它保證了模塊版本的不變性)

系統依賴

系統範圍的依賴應該是一直可用,而且Maven不會去倉庫中查找。系統範圍依賴一般是指JDK或者VM提供的依賴。因此,系統依賴適用於這種狀況:之前能夠單獨獲取,但如今是由JDK提供的依賴。典型的例子就是JDBC標準擴展或者Java認證和受權服務(JAAS)。一個簡單的例子以下:

<project>

<dependencies>
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
</dependencies>

</project>

若是你的構件依賴於JDK的tools.jar,那麼系統路徑的值以下所示:

<project>

<dependencies>
<dependency>
<groupId>sun.jdk</groupId>
<artifactId>tools</artifactId>
<version>1.5.0</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>

</project>

原創文章,轉載請註明: 轉載自併發編程網 – ifeve.com本文連接地址: 《Maven官方文檔》-Maven依賴機制簡介

Tyrian

Software Engineerat TP-LINK

Favorite添加本文到個人收藏

Related Posts:

  1. 《Maven官方文檔》建立Archetype
  2. 《Maven官方文檔》POM文件
  3. 《maven官方文檔》5分鐘開始Maven
  4. 《Maven官方指南》可選的依賴和依賴排除
  5. 《Maven官方文檔》什麼是原型(Archrtype)?
  6. spring boot集成mongodb最簡單版
  7. Maven入門指南(二)
  8. Apache Storm 官方文檔 —— 使用 Maven 構建 Storm 應用
  9. Spring Boot 集成 FreeMarker 詳解案例
  10. Springboot 整合 Dubbo/ZooKeeper 詳解 SOA 案例
  11. 《KAFKA官方文檔》5.2 APIs
  12. Maven的Java插件開發指南
  13. Maven入門指南(一)
  14. Spring Boot 整合 Mybatis 實現 Druid 多數據源詳解
  15. Spring Boot 整合 Redis 實現緩存操做
相關文章
相關標籤/搜索