依賴機制是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(*)
–
(*)注意:這裡本來應該是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> <artifactid>artifact-b</artifactid> <type>bar</type> <scope>runtime</scope> </dependencies> </project>
項目b
<type>war</type>
這兩個pom都依賴于同一個子產品,同時每個pom又各自依賴于一個無關的子產品。父項目的pom詳細資訊如下所示:
<dependencymanagement> </dependencymanagement>
這樣兩個子項目的pom檔案就簡單多了。
<!– this is not a jar dependency, so we must specify type. –>
注意:在這兩個pom檔案的依賴中,我們必須指定<type/>元素。因為與依賴管理元素比對的依賴引用最小資訊集是{groupid, artifactid, type, classfier}。許多情況下,依賴指向的jar不需要指定classfier。因為預設type是jar,預設classfiler為空,是以我們可以把資訊集設定為{groupid, artifactid}。
依賴管理元素第二個非常有用的功能是控制傳遞性依賴中構件的版本。例子如下
項目a:
<modelversion>4.0.0</modelversion> <groupid>maven</groupid> <artifactid>a</artifactid> <packaging>pom</packaging> <name>a</name> <groupid>test</groupid> <version>1.2</version> <artifactid>b</artifactid> <scope>compile</scope> <artifactid>c</artifactid> <artifactid>d</artifactid>
項目b:
<parent> </parent> <name>b</name>
當在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>元素的構件來實作。