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一般按照倉庫優先級順序下載依賴包,但如果中途報錯(例如,依賴包編譯有問題),則拋出異常,結束生命週期,即使之後的依賴包可能是可以正常下載和編譯的。
最終給出倉庫優先級部分的流程圖如下所示: