Apache Maven --- [依賴機制]

依賴機制介紹html

依賴管理是Maven衆所周知的特性之一,也是Maven擅長的領域之一.管理單個項目的依賴並非太困難,可是當你開始處理由數十個甚至上百個模塊組成的多模塊項目或者應用時,Maven將會很好的幫助你保持項目的高可控性和穩定性.java

傳遞依賴sql

傳遞依賴是Maven 2.0 的新特性.它讓你再也不須要發現並指定你所須要的依賴庫並自動包含它們.apache

這個特性是經過從你指定的遠程倉庫閱讀你所依賴的項目文件來簡化的.一般來說,那些項目的全部依賴被使用在你的項目中,它們可能繼承自它們的父項目或者來自它們的依賴等等.maven

依賴能夠彙集的層級數量沒有限制,只有在發現循環依賴時纔會引起問題.ide

因爲依賴的傳遞,包含庫的圖標能夠迅速膨脹到至關大.鑑於這個緣由,這裏有一些額外的特性將會限制依賴的包含:測試

依賴調解 - 它決定了使用什麼版本的依賴當遭遇到一個工件的多個版本時.通常的,Maven 2.0 只支持"最近定義",這意味着它會採用依賴樹中最近的依賴版本提供給你的項目.你也能夠經過在你的POM中顯示聲明一個依賴來確保它的版本.注意,若是兩個依賴版本在依賴樹中的相同層次,直到 Maven 2.0.8 它都沒有準肯定義哪個將勝出,可是自從 Maven 2.0.9 開始宣佈第一個聲明將勝出.("最近定義"意味着被採用的依賴版本將會是在依賴樹中離你項目最近的那個,例如:依賴被定義成A->B->C->D 2.0 和A->E->D 1.0,那麼當你構建A時,D1.0將會被採用,由於它的線路比較短.你能夠在POM中顯示聲明D 2.0來強制指定版本).插件

依賴管理 - 它容許項目做者直接指定工件的使用版本當他們在傳遞依賴或者在那些沒有指定版本的依賴中遭遇時.在前面部分的例子中有一個依賴直接添加給了A儘管A沒有直接使用它.取而代之的是,A能直接包含D做爲依賴在依賴管理部分而且直接控制D要使用的版本.code

依賴範圍 - 這容許你只爲當前構建階段包含適當的依賴.後面會詳細描述.xml

排除依賴 - 若是X依賴Y,Y依賴Z,那麼X的全部者能夠經過"exclusion"元素來顯式排除對Z的依賴.

可選依賴 - 若是Y依賴Z,Y的全部者能夠經過"optional"元素來標記Z做爲可選依賴.當X依賴Y時,X就只會依賴Y而不會依賴Z.X的全部者能夠顯式添加Z做爲依賴.(把可選依賴想象成"默認的依賴排除"有助於理解).

依賴範圍

依賴範圍用來限制依賴的傳遞,也影響各類構建任務使用的類路徑.

這裏有6種範圍:

compile

這是默認範圍,當沒有指定範圍的時候默認使用它.這些依賴在項目的全部類路徑下都是可用的,另外,它們會傳播到依賴它們的項目中.

provided

這個compile很像,可是它代表你指望在運行時提供給依賴的JDK或者一個容器.例如,當構建一個J2EE的Web應用的時候,你將會設置Servlet API和相關的J2EE API依賴範圍爲provided由於Web容器提供這些類.這個範圍只有在編譯和測試類路徑可用,並且它不會傳遞.

runtime

這個範圍代表編譯的時候不須要這些依賴,可是執行的時候須要.它們會在運行時和測試類路徑下,可是不會在編譯類路徑下.

test

這個範圍代表一般的使用狀況下都不須要這些依賴,它們只針對測試編譯和執行階段有用.

system

這個範圍和provided相似除了你不得不顯式提供包含它的JAR.這個工件老是可用的並且它不會在倉庫中進行查找

import(Maven 2.0.9+)

這個範圍只能被用在<dependencyManagement>部分中類型爲pom的依賴.它代表指定的POM應該被替換成在那個POM中 <dependencyManagement>部分裏的依賴.由於它們用於替換,因此範圍是import的依賴它們實際不參與限制依賴的傳遞.

上述每一種範圍(除了import)都以不一樣的方式來影響依賴傳遞,下面的表格演示了這種影響.第一列表明一個依賴的設置範圍,第一行表示那個依賴傳遞通過的依賴範圍,交匯處表示結果範圍.若是沒有結果範圍被列出,表示這個依賴將被忽略.

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

請注意:(*)意指這應該被runtime範圍取代,志抑鬱於全部compile依賴必須被顯式的羅列出來.可是,存在這樣的狀況,你所依賴的庫繼承自另外一個庫的類,會強制你在編譯時期就可用.因爲這個緣由,在編譯時期這些依賴保持compile範圍甚至當它們是可傳遞的時候.

依賴管理

依賴管理部分是一個集中化依賴信息的機制.當你有一堆項目都繼承了一個通用的父項目,那麼這就能夠把全部依賴信息放進父項目的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:

<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>

注意:這些依賴中有兩個,咱們必須指定<type>元素.這是由於在<dependencyManagement>部分用於匹配一個依賴引用的最小信息集合其實是{groupId,artifactId,type,classifier}.在不少狀況下,這些依賴將引用沒有classifier的jar工件,這就容許咱們能夠將標識集合簡寫成{groupId,artifactId},由於type的默認值就是jar,而classifier的默認值是null.

第二點,也是很是重要的關於依賴管理的用處是用來在傳遞依賴中控制工件的版本.做爲一個示例,考慮這些項目:

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

當在項目B上運行Maven的時候,a,b,c和d的1.0版本將會被使用不論在它們的pom中指定的版本是什麼:

a和c都做爲項目的依賴被聲明因此依賴調解使用版本1.0是能夠預見的.並且它們也都具備runtime的範圍由於被直接指定了.

b定義在項目B的父項目中的依賴管理部分並且由於依賴管理對傳遞依賴的優先級高於依賴調解,

因此版本1.0將被選擇,b也將會有compile範圍.

最後,由於d被指定在項目B的依賴管理部分,d應該是a或c的一個依賴,版本1.0將會被再次選擇,由於依賴管理的優先級高於依賴調解並且也由於當前pom的聲明優先級高於它的父pom的聲明.

可用的依賴管理標籤的參考信息請查閱:https://maven.apache.org/ref/3.3.9/maven-model/maven.html#class_DependencyManagement

導入依賴

這個特性只有在Maven 2.0.9或者以後的版本可用.這意味着poms聲明範圍import在早期的Maven版本中不能被解析.在你決定使用它以前請仔細考量這個信息,若是你決定使用它,咱們建議你使用強制插件來要求至少2.0.9的Maven版本.

以前的例子描述瞭如何經過繼承來指定託管的依賴.然而,在大型項目中這基本上是不能實現的由於一個項目只能繼承自一個父項目.爲了實現這一點,項目能夠從其餘項目導入托管的依賴.這經過描述一個帶有範圍"import"的pom工件來做爲依賴來實現.

項目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是以前例子中定義過的,最終結果將是同樣的.全部A管理的依賴都將被合併到B中除了d,由於d在這個POM中已經定義了.

項目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.在這裏,a版本1.1將被使用,由於X是首先聲明的,並且a沒有在Z的依賴管理中聲明.

這個過程是遞歸的.例如,若是X導入了另外一個pom,Q,當Z在處理的時候,你會發現Q的全部依賴被定義在X中.

導入是頗有效的當用來定義由一系列相關的工件構成的一個"庫"時.一個項目使用來自這些"庫"的一個或多個工件是至關常見的.然而,有時候很難保持在項目中使用的工件版本和分佈在那些庫裏的版本的同步一致.下面的模式闡明瞭如何建立一個材料清單(BOM)來供其餘項目使用.

項目的根是BOM pom.它定義了全部將會被建立在庫裏的工件的版本.其餘項目想要使用這些庫應該導入這個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>

子項目用BOM做爲它的父項目.

<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>bom</artifactId>
  </parent>
 
  <groupId>com.test</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>
 
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
      </dependency>
      <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.1</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <modules>
    <module>project1</module>
    <module>project2</module>
  </modules>
</project>

接下來是真正的項目POMs

<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.無法處理循環並將致使拋出異常.

當引用了那些pom中有傳遞依賴的工件時,項目將須要爲那些工件指定版本做爲託管依賴.不這麼作的話將會致使構建失敗由於工件沒有指定的版本.(在任何狀況下這都應該被認爲是一個最佳實踐,由於它防止工件的版本從一個構建到下一個構建的變化)

系統依賴

範圍指定爲"system"的依賴老是可用的並且它們不會在倉庫中查找.它們一般用來告訴Maven那些由JDK或者VM提供的依賴.所以,系統依賴對於解決那些如今由JDK提供,可是又能夠提早獨立下載的依賴是頗有用的.典型的示例是JDBC的標準擴展和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>
相關文章
相關標籤/搜索