Maven對後端開發的人員來說並不陌生,但多數情況下也僅僅只是配置了鏡像地址用來下載依賴包。真正遇到難解的問題,還是得要稍微深入理解一些知識例如POM、座標、倉庫、生命週期等原理,這裏推薦《Maven實戰》這本書,雖然很老但是很經典。

今天想總結一下倉庫的優先級和失敗機制的問題,啓發內容來自於這篇博客:Maven倉庫理解和優先級,作者的核心觀點大致如下:

本地倉庫 > 私服 (profile)> 遠程倉庫(repository)和 鏡像 (mirror) > 中央倉庫 (central)

鏡像是一個特殊的配置,其實鏡像等同與遠程倉庫,沒有匹配遠程倉庫的鏡像就毫無作用。所以 maven 倉庫真正的優先級爲:

本地倉庫 > 私服(profile)> 遠程倉庫(repository)


接下來,通過實戰進行驗證和理解:

1、初始化Maven項目

首先,有兩個點需要提出:

  • IDEA中的maven-reimport指令,默認使用的是用戶級別的settings.xml配置,但是隻在background tasks中執行,報錯信息不明顯,因此不推薦;而命令行中以mvn執行的指令,默認使用全局的settings.xml配置,可以通過在conf文件夾下的m2.conf文件中進行配置。
  • 因此,一般是推薦全局配置和用戶配置保持一致,或者IDEA調整User Settings file的配置,本文采用後者(根據實際情況判斷,也有可能因爲配置了環境變量等原因,命令行默認使用用戶配置)。

settings.xml中內容如下,沒有配置鏡像中央倉庫,但是配置了私服(jdk1.7-Mac下使用Nexus搭建Maven私服,jdk-1.8-Mac安裝配置nexus3.0):

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <pluginGroups></pluginGroups>
  <proxies></proxies>
  <servers></servers>
  <mirrors></mirrors>
  <profiles>
    <profile>
      <id>mymaven</id>
      <repositories>
        <repository>
          <id>nexus maven</id>
          <url>http://localhost:8081/repository/maven-public/</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>mymaven</activeProfile>
  </activeProfiles>
</settings>

pom.xml中的依賴和倉庫配置如下:

<?xml version="1.0" encoding="UTF-8"?>

<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.mavenTest</groupId>
  <artifactId>MavenTest</artifactId>
  <version>1.0-SNAPSHOT</version>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.3.1.RELEASE</version>
    </dependency>
  </dependencies>

  <repositories><!-- 代碼庫 -->
    <repository>
      <id>jboss maven</id>
      <url>http://repository.jboss.org/nexus/content/groups/public/</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
        <updatePolicy>always</updatePolicy>
        <checksumPolicy>fail</checksumPolicy>
       </snapshots>
     </repository>
  </repositories>
</project>

命令行中使用”mvn clean install”指令,失敗了,報錯如下:

Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:15 min
[INFO] Finished at: 2018-09-19T00:45:43+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Plugin org.apache.maven.plugins:maven-clean-plugin:2.5 or one of its dependencies could not be resolved: Failed to read artifact descriptor for org.apache.maven.plugins:maven-clean-plugin:jar:2.5: Could not transfer artifact org.apache.maven.plugins:maven-clean-plugin:pom:2.5 from/to central (https://repo.maven.apache.org/maven2): Connect to repo.maven.apache.org:443 [repo.maven.apache.org/151.101.40.215] failed: Operation timed out -> [Help 1]

雖然配置的私服中有該插件,但是clean插件還是選擇了默認的中央倉庫下載(因爲沒有配置中央鏡像),而因爲網絡問題最終導致了失敗。這有兩種可能,一種就是中央倉庫的優先級高於profile中定義的私服和pom中定義的其他遠程倉庫,另一種則是隻有插件是默認使用遠程倉庫下載的。

2、私服與中央倉庫的優先級比較

按照如上的推理,可以通過在全局settings.xml中增加阿里雲中央鏡像,然後下載插件和pom中的依賴包,觀察輸出信息從而得出結論。

添加了阿里雲中央鏡像的settings.xml內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <pluginGroups></pluginGroups>
  <proxies></proxies>
  <servers></servers>
  <mirrors>
    <mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
  <profiles>
    <profile>
      <id>mymaven</id>
      <repositories>
        <repository>
          <id>nexus maven</id>
          <url>http://localhost:8081/repository/maven-public/</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>mymaven</activeProfile>
  </activeProfiles>
</settings>

pom中內容保持不變,執行命令行 “mvn clean install”,最終build success,關鍵信息如下:

Downloading from alimaven: http://maven.aliyun.com/nexus/content/groups/public/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2.4.jar
Downloaded from alimaven: http://maven.aliyun.com/nexus/content/groups/public/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2.4.jar (27 kB at 106 kB/s)
Downloading from nexus maven: http://localhost:8081/repository/maven-public/org/springframework/spring-core/4.3.1.RELEASE/spring-core-4.3.1.RELEASE.pom
Downloaded from nexus maven: http://localhost:8081/repository/maven-public/org/springframework/spring-core/4.3.1.RELEASE/spring-core-4.3.1.RELEASE.pom (2.5 kB at 31 kB/s)
Downloading from nexus maven: http://localhost:8081/repository/maven-public/commons-logging/commons-logging/1.2/commons-logging-1.2.pom
.
.
.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 57.884 s
[INFO] Finished at: 2018-09-19T01:03:35+08:00
[INFO] ------------------------------------------------------------------------

發現plugin插件都是從阿里雲鏡像(代表中央倉庫的優先級)下載的,而pom中的spring依賴包卻是在阿里雲鏡像、jboss鏡像中存在的情況下通過私服下載。因此,能得出結論,Maven中插件默認通過中央倉庫進行下載,而其他依賴包下載時profile中的私服優先級大於中央倉庫和pom中的其他遠程倉庫。

3、pom中其他遠程倉庫與中央倉庫的優先級比較

在遠程倉庫中,除了鏡像會覆蓋攔截對應的遠程倉庫,遠程倉庫本身也有pom中定義的其他遠程倉庫和中央倉庫,他們的優先級仍然需要進一步驗證。

將settings.xml中的私服配置刪除,保留中央鏡像,內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <pluginGroups></pluginGroups>
  <proxies></proxies>
  <servers></servers>
  <mirrors>
    <mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
  <profiles></profiles>
</settings>

pom中內容不變,運行指令”mvn clean install”,結果的關鍵信息如下:

Downloading from jboss maven: http://repository.jboss.org/nexus/content/groups/public/org/springframework/spring-core/4.3.1.RELEASE/spring-core-4.3.1.RELEASE.pom
Downloaded from jboss maven: http://repository.jboss.org/nexus/content/groups/public/org/springframework/spring-core/4.3.1.RELEASE/spring-core-4.3.1.RELEASE.pom (2.5 kB at 926 B/s)
Downloading from jboss maven: http://repository.jboss.org/nexus/content/groups/public/commons-logging/commons-logging/1.2/commons-logging-1.2.pom
Downloading from alimaven: http://maven.aliyun.com/nexus/content/groups/public/commons-logging/commons-logging/1.2/commons-logging-1.2.pom
.
.
.
 ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.083 s
[INFO] Finished at: 2018-09-19T01:17:14+08:00
[INFO] ------------------------------------------------------------------------

可以看出優先從pom中定義的jboss倉庫中下載,但因爲網絡問題,部分依賴包在jboss下載失敗的情況下,去了阿里雲中央鏡像中下載。這說明,遠程倉庫中pom中的其他遠程倉庫優先級要高於中央倉庫。

實際上這個優先級的本質爲子pom優先級高於父pom,因爲在Maven中默認的中央倉庫定義在根pom中,所有的子pom繼承自根pom,跟Java中的Object類類似。

可以進一步驗證,同一個pom中的多個倉庫,對每個依賴按照聲明的順序,然後結合網絡連接情況,最終選擇下載地址。

4、Maven的失敗機制

可以繼續通過如上的方法進行測試和驗證,總結Maven的失敗機制如下:

  • Maven找不到包時,則會按照私服、pom中的遠程倉庫之後中央倉庫的順序逐層查找,最終仍然沒有找到或者網絡問題則報
  • 如果中途出現了重大異常,則採用快速失敗機制
  • Maven包在Release版本和SNAPSHOT版本上的策略有一定的區別,主要是SNAPSHOT版本的依賴包多了時間戳比較的過程

5、總結

  • 使用mvn命令時候默認用的是全局settings.xml(少部分例外,如mac電腦上默認使用用戶的settings.xml),而在IDEA中使用reimport時候用的是用戶settings.xml;所以一般要求兩個文件保持一致,也不推薦將IDEA配置改爲全局settings.xml,因爲升級會帶來全局配置重置。
  • 依賴包下載時私服優先級高於pom中的遠程倉庫和中央倉庫,插件下載默認使用中央倉庫(或者其鏡像)。
  • pom中的遠程倉庫優先級高於中央倉庫,本質爲子pom優先級高於父pom(默認的中央倉庫定義在根pom中,所有的pom繼承自根pom,與Java的Object類相似)。
  • 同一個pom中的多個遠程倉庫,對每個依賴包首先按照聲明的順序,然後根據網絡連接情況,最終選擇下載倉庫。
  • Maven一般按照倉庫優先級順序下載依賴包,但如果中途報錯(例如,依賴包編譯有問題),則拋出異常,結束生命週期,即使之後的依賴包可能是可以正常下載和編譯的。

最終給出倉庫優先級部分的流程圖如下所示: