Maven依賴解析


 本文將記錄Maven工程中依賴解析機制,內容包括:

  1. Maven依賴基本結構
  2. 從倉庫解析依賴的機制
  3. 依賴傳遞性解析實例

1. Maven依賴基本結構

上篇文章記錄了Maven依賴的聚合與繼承,POM中依賴的聲明通過dependency進行定義,並且通過groupId、artifactId及version三項定位Maven庫中的唯一依賴。除了這三項外,還有其他屬性進行限制,如下:

 1 <dependencies>
 2    <dependency>
 3      <groupId>...</groupId>
 4      <artifactId>...</artifactId>
 5      <version>...</version>
 6      <type>...</type>
 7      <scope>...</scope>
 8      <optional>...</optional>
 9      <exclusions>
10         <exclusion>
11             <groupId>...</groupId>
12             <artifactId>...</artifactId>
13         </exclusion>
14      </exclusions>
15    </dependency>
16 </dependencies>
  • groupId、artifactIdversion三項不再敘說;
  • type:依賴類型,對應於項目坐標定義的packaging,默認為jar;
  • scope:依賴范圍,包括compile、test、runtime、import、provided、system;
  • optional:標記依賴為可選,即依賴沒有傳遞性;
  • exclusions:排除傳遞性依賴

1.1 依賴范圍

  我們知道,Maven工程約定具有固定的目錄結構,以便於Maven各插件對工程處理,如編譯(compile)插件,會將src/main/java中的類編譯到target/classes目錄下,則編譯對應的classpath即為target/classes。依賴范圍就是控制依賴於編譯classpath(target/classes)、測試classpath(target/test-classes)和運行classpath(以Web工程為例,WEB-INF/classes)的關系。具體依賴范圍的講述可參考官網文檔,在此僅進行稍微總結:

  • compile:編譯依賴范圍,對編譯、測試、運行classpath都有效,為默認范圍;
  • test:測試依賴范圍,僅對測試classpath有效;
  • runtime:運行時依賴范圍,對測試和運行classpath有效,對編譯無效,如JDBC的依賴引入,因為JDK中值聲明了JDBC接口,具體實現由各廠家決定;
  • import:導入依賴范圍,對三種classpath不產生實際影響,一般是導入pom類型的依賴,聚合情況下需要聲明打包類型為pom,其中可包含dependencyManagement(如上一篇文章),此時對引入該依賴的工程不產生影響;
  • provided:已提供依賴范圍,對測試和編譯classpath有效,對運行時無效,如servlet-api在測試或編譯的時候需要,在運行的時候由容器提供,故不需要重復引入;
  • system:系統依賴范圍,與provided范圍一致,需要明確指定該jar包,其不在Maven倉庫中,感覺不太常用。

1.2 依賴傳遞性

依賴傳遞性,舉例說明,比如A引用B,B引用C,正常情況下,A也會引用C依賴,即A經過傳遞間接引用了C(依賴為可選時,需另行處理)。具體不同范圍的依賴經過傳遞后,其依賴范圍的變化如下邊(從官網扣下來的)

依賴傳遞的准則

  • 路徑最近這優先,即在引用傳遞鏈上,獲取出離本POM最近的傳遞性依賴;
  • 如果路徑距離相同,則以取POM中的聲明順序靠前的。

1.3 可選依賴

   可選依賴不被傳遞。假設項目A依賴於B,B依賴於X和Y,並且X和Y聲明為可選,則X和Y對A就不具有傳遞性了。如果A需要依賴於X或Y,則需要直接引用。

1.4 依賴排除

   假設項目A依賴於B,B依賴於C(版本為1.0),此時A會傳遞性依賴於C(1.0)。如果A需要引用C(2.0版本),存在兩種情況:

  (1)A直接引用C(2.0),則可以不對B依賴添加exclusions元素;

  (2)A在引用B的同時,還引用D(D引用C(2.0)),則可以在引用B的時候使用exclusions元素將C(1.0)排除,也可以將D依賴聲明在B之前。

2. 從倉庫解析依賴的機制

   依賴解析的基本過程:當本地倉庫中沒有依賴構件,則Maven從遠程倉庫中下載;當依賴版本為快照版本時,Maven會自動計算最新的快照,並引用。

  背后的依賴解析機制概括如下:

  (1)當依賴的范圍為system,則從本機文件系統中解析構件;

  (2)根據依賴坐標計算定位依賴位置后,嘗試從本地倉庫尋找依賴,若找到,則解析成功;

  (3)若本地倉庫沒有對應構件,則遍歷所有遠程倉庫,發現后解析下載;

  (4)如果依賴的版本為RELEASE或LATEST,則讀取所有遠程倉庫的元數據groupId/artifactId/version/maven-metadata.xml,與本地元數據合並后,計算出RELEASE或LATEST的真實值,然后基於真實值檢查本地倉庫和遠程倉庫,如步驟(2)(3);

  (5)如果依賴的版本為SNAPSHOT,類似的,讀取遠程倉庫的元數據,並與本地元數據合並,計算出最新版本的快照,再從本地倉庫和遠程倉庫檢索。

 下邊為一個快照版本依賴的元數據maven-metadata-local.xml,包括最近更新時間戳,以及存在的不同版本:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <groupId>com.test</groupId>
  <artifactId>C</artifactId>
  <versioning>
    <versions>
      <version>1.0-SNAPSHOT</version>
      <version>2.0-SNAPSHOT</version>
    </versions>
    <lastUpdated>20171113125841</lastUpdated>
  </versioning>
</metadata>

 對應文件目錄結構如下:

其中每個版本中,包含對自身描述的元數據。以2.0-SNAPSHOT為例,其目錄結構如下,主要包含jar和pom兩部分,maven-metadata-local.xml為依賴元數據:

具體元數據內容如下,記錄了該版本依賴最近一次的更新時間。在本地庫中沒有該構件的時候,會檢索所有遠程倉庫,結合元數據文件,計算最新的版本;或者在進行強制更新的時候,也會計算出最新版本

<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
  <groupId>com.test</groupId>
  <artifactId>C</artifactId>
  <version>2.0-SNAPSHOT</version>
  <versioning>
    <snapshot>
      <localCopy>true</localCopy>
    </snapshot>
    <lastUpdated>20171117132322</lastUpdated>
    <snapshotVersions>
      <snapshotVersion>
        <extension>jar</extension>
        <value>2.0-SNAPSHOT</value>
        <updated>20171117132322</updated>
      </snapshotVersion>
      <snapshotVersion>
        <extension>pom</extension>
        <value>2.0-SNAPSHOT</value>
        <updated>20171117132322</updated>
      </snapshotVersion>
    </snapshotVersions>
  </versioning>
</metadata>

3. 依賴傳遞性解析實例

   創建3個Maven工程A、B、C(命令mvn archetype:generate)。

  (1)編輯各自的POM文件,使其依賴關系如下圖所示

                    

其中B為A的直接依賴,C具有傳遞性,為A的傳遞性依賴,分析其依賴樹可以直觀的看出依賴關系(mvn dependency:tree),如下:

  • B依賴於C(1.0)

  •  A依賴於B(1.0),間接依賴C(1.0)

(2)將B對的C的依賴改為optional,即可選的,此時A的依賴樹如下,不包括C:

(3)修改POM文件,使其依賴關系如下:

          

 看A的依賴樹,如下:

 綜上,正常情況下依賴是具有傳遞性,除非聲明為optional。

總結:

  • 聲明依賴時,除了常用的三項位置元素,還具有包括范圍、類型、可選和排除等;
  • 依賴具有傳遞性,具有路徑優先的約定;
  • 當引用多個工程時,會潛在的引用其他依賴,需要注意是否會引錯包,或者沖突;
  • Maven依賴解析,對於本地庫中沒有的構件,Maven會綜合遠程倉庫與本地倉庫的元數據,計算最新版本后,再引用。

參考:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM