夜雨聆风学习资料网

ARTICLE · 1304690

Maven 深入:构建、依赖与镜像下载流程

Maven 深入:构建、依赖与镜像下载流程

💡 导语:日常敲 mvn package 时,你有没有想过:一个依赖 JAR 到底是从哪里下载下来的?配了镜像之后,Maven 访问的又是谁?这篇文章把 Maven 的坐标、生命周期、依赖管理和镜像匹配机制一次讲透.

日常开发中,Maven 项目通过一个根 pom.xml 管理多个模块:根 POM 的 packaging 为 pom,在 <modules> 中列出 spring-boot-configuration、websocket、http-clients 等模块;子模块通过 <parent> 继承根 POM 中的 Java 版本、编码和代码检查插件配置。

一、Maven 的核心概念

概念
含义
典型示例
坐标(GAV)
groupId:artifactId:version
,用于定位仓库中的构件
com.example.learn:learn:1.0.0
构件(Artifact)
JAR、POM、WAR 等可被 Maven 管理的产物
Spring Boot 依赖、业务模块构建产物
POM
项目的构建描述文件,定义依赖、插件、父子关系和模块
根 pom.xml 列出子模块并配置公共构建规则
父 POM
供子模块继承 properties、pluginManagement、依赖版本约束等
子模块继承根工程 learn
聚合(Aggregation)
用 <modules> 将多个模块放在一起构建
根目录执行 mvn verify 可构建所有模块
Reactor
多模块构建中按依赖图排序并构建模块
模块 A 依赖模块 B 时,B 会先被构建,无需先 install

定位构件时,除了 GAV,还可能用到 classifier 和 extension/type,例如测试 JAR 常带 classifier=tests。另外,不同仓库中的同一 GAV 也可能对应不同内容,排查依赖问题时要留意下载来源。

二、生命周期与常用命令

validate → compile → test → package → verify → install → deploy 

上面是默认生命周期中的几个常用阶段。执行某个阶段时,Maven 会先执行它之前的所有阶段:例如运行 mvn package,会先校验、编译、测试,再打包;运行 mvn verify,则会在打包后继续执行验证。很多项目会在 validate 阶段检查代码格式和 import 排序,在 verify 阶段执行 PMD 检查。

命令
用途
mvn validate
校验 POM、插件和构建前置条件
mvn compile
编译主源码
mvn test
编译并执行测试
mvn package
打包到模块的 target/ 目录
mvn verify
构建并执行验证(如 PMD 检查),可在提交前运行
mvn install
将构件写入本机 Maven 仓库,仅供本机其他工程使用
mvn deploy
将构件发布到远程制品库
mvn dependency:tree -Dverbose
查看依赖树、传递路径与冲突版本
mvn help:effective-pom
查看继承、Profile 合并后最终生效的 POM

三、依赖管理与作用域

模块要使用某个依赖,需要在 <dependencies> 中声明。<dependencyManagement> 用来管理版本、scope 和排除项,本身不会引入依赖。多模块项目通常把公共依赖版本放在父 POM 的 dependencyManagement 或 BOM 中,各子模块再声明自己用到的依赖。

scope
编译 / 测试 / 运行
常见用途
compile
(默认)
是 / 是 / 是
业务代码直接使用的库
provided
是 / 是 / 否
容器或运行环境提供的 API、Lombok
runtime
否 / 是 / 是
数据库驱动等只需运行时加载的依赖
test
否 / 是 / 否
JUnit、Mockito、测试工具

遇到版本冲突,可以先运行 mvn dependency:tree -Dverbose,看看不同版本分别由哪些依赖引入,再通过 BOM 或 dependencyManagement 统一版本,或用 <exclusions> 排除不需要的传递依赖。

删除 .m2 中的缓存并不会改变依赖关系,重新下载后仍会出现同样的冲突。冲突要在 POM 层面解决,而不是靠清缓存。

四、一个依赖 JAR 是如何下载的

以 org.springframework.boot:spring-boot-starter-web:3.3.4 为例:Maven 从项目 POM 和父 POM 中确定依赖坐标,再读取这个依赖的 POM,继续查找它依赖的其他构件。解析过程中需要用到各依赖的 POM、JAR,以及相关的校验文件和元数据;本地缺少的文件会从远程仓库下载。

完整流程如下图:

图中的本地仓库通常位于开发机的 .m2/repository。配置镜像后,下载地址变了,但下载的构件仍然保存在本地仓库,后续构建可以继续使用。

五、有镜像时,Maven 到底访问谁

镜像通常配置在 ~/.m2/settings.xml 的 <mirrors> 中,Windows 下的路径一般是 C:\Users\<用户名>\.m2\settings.xml。其中:

  • mirrorOf
     用来匹配原仓库的 ID
  • <mirror>
     自身的 <id> 只是这个镜像的名称
  • 仓库 URL 和依赖的 groupId、artifactId 都不参与这项匹配

例如 Maven 默认的 Central 仓库 ID 是 central;项目 POM 中也可以定义其他仓库:

<repositories><repository><id>company-releases</id><url>https://packages.example.com/maven/releases</url></repository></repositories>

Maven 准备访问这些仓库时,会分别用 central、company-releases 去匹配 mirrorOf。

5.1 匹配顺序

Maven 为每个待访问的仓库选择一个镜像,查找顺序如下:

  1. 优先找 mirrorOf 与仓库 ID 完全相同的镜像
    。例如仓库 ID 为 central,则 mirrorOf=central 直接命中。
  2. 没有精确匹配时,再按 settings.xml 中 <mirrors> 的声明顺序,查找第一个符合通配符、列表或排除规则的镜像。

选中后,Maven 直接请求镜像 URL。即使请求失败,也不会为同一个原仓库继续尝试其他镜像。

如果镜像使用 Nexus 或 Artifactory,能从它下载哪些依赖,还要看服务端配置了哪些仓库和上游代理。mirrorOf 只决定请求发往哪里。

5.2 mirrorOf 写法与匹配结果

mirrorOf
 值
匹配规则
示例用途
central
只匹配 ID 为 central 的仓库
只替换 Maven Central
company-releases
只匹配项目定义的 company-releases
将某个企业仓库替换为备用地址
central,company-releases
匹配两个指定 ID(英文逗号分隔)
同一个 Nexus group 同时代理这两个仓库
*
匹配全部仓库 ID
企业统一使用一个 Nexus/Artifactory 公共代理
*,!company-releases
匹配所有仓库,但排除 company-releases
公共依赖走代理,企业 Release 库直连
external:*
匹配非 localhost 且非 file: 的仓库
保留本机集成测试仓库,其他远程请求走代理
external:http:*
Maven 3.8.0+:匹配非 localhost 的 HTTP 仓库
为外部 HTTP 仓库配置统一镜像

多个规则用英文逗号分隔,中间不要加空格。例如 *,!company-releases 会正确排除 company-releases;写成 *, !company-releases 后,空格会使排除规则无法按预期匹配。

5.3 具体配置举例

只替换 Central 时,指定它的仓库 ID:

<mirror><id>central-proxy</id><url>https://nexus.example.com/repository/maven-central/</url><mirrorOf>central</mirrorOf></mirror>

这样,访问 central 的请求会发往 central-proxy,POM 中的 company-releases 不受影响。

所有仓库都通过公司私服访问,可以使用 *:

<mirror><id>company-public</id><url>https://nexus.example.com/repository/maven-public/</url><mirrorOf>*</mirrorOf></mirror>

在这个配置下,Maven 会直接访问 company-public,不会先请求 Central。所需的 Central、企业库和第三方库构件,都需要由这个私服仓库组提供或代理。

内部发布库需要直连(如有独立权限或网络入口),可以将它排除:

<mirror><id>company-public</id><url>https://nexus.example.com/repository/maven-public/</url><mirrorOf>*,!company-releases</mirrorOf></mirror>

此时,central 和第三方仓库等仍通过 company-public 访问,company-releases 则使用 POM 或 Profile 中声明的原始 URL。

给 Central 单独配镜像,其余仓库用另一个镜像:

<mirrors><mirror><id>central-only</id><url>https://nexus.example.com/repository/maven-central/</url><mirrorOf>central</mirrorOf></mirror><mirror><id>all-other-repos</id><url>https://nexus.example.com/repository/maven-public/</url><mirrorOf>*</mirrorOf></mirror></mirrors>

central 会匹配 central-only,company-releases 等其他仓库会匹配 all-other-repos。精确匹配优先于通配符匹配;如果没有精确匹配,就按声明顺序使用第一个符合条件的规则。

5.4 如何确认实际命中哪个镜像

mvnhelp:effective-settings  mvn-Xdependency:resolve 

5.5 依赖不在 Central,而在其他仓库时

假设项目需要 com.vendor:payment-sdk:1.2.0,Central 中没有这个依赖,厂商提供的仓库地址是 https://repo.vendor.example/maven/releases/。可以在父 POM 中声明这个仓库让子模块继承,也可以把它配置在团队共用的 settings.xml Profile 中:

<repositories><repository><!-- 这是原始仓库 ID,供 mirrorOf 与 server 凭据匹配 --><id>vendor-releases</id><name>VendorReleaseRepository</name><url>https://repo.vendor.example/maven/releases/</url><releases><enabled>true</enabled></releases><snapshots><enabled>false</enabled></snapshots></repository></repositories>

依赖本身的声明方式不变:

<dependency><groupId>com.vendor</groupId><artifactId>payment-sdk</artifactId><version>1.2.0</version></dependency>

如果厂商的 Release 和 Snapshot 使用不同的 URL,就分别配置两个仓库。上面的配置只启用了 Release;快照仓库则将 releases.enabled 设为 false、snapshots.enabled 设为 true。如果同一个 URL 同时提供两类构件,可以在一个仓库配置中同时启用两者:

<repository><id>vendor-all</id><url>https://repo.vendor.example/maven/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository>

这两个 enabled 开关决定 Maven 是否向该地址查询 Release 或 Snapshot。开启查询后能否下载到构件,还要看服务端是否提供它。两类构件的更新策略也可以分别配置。

仓库需要认证时,在 settings.xml 的 <servers> 中配置凭据,server.id 要与实际访问的仓库 ID 一致。直连 vendor-releases 时可以这样写:

<servers><server><id>vendor-releases</id><username>${env.VENDOR_REPO_USERNAME}</username><password>${env.VENDOR_REPO_TOKEN}</password></server></servers>

配置了 mirrorOf=* 时怎么办

如果 settings.xml 中配置了 <mirrorOf>*</mirrorOf>,新加的 vendor-releases 也会被匹配,请求会发往公司镜像。因此,只在 POM 中添加厂商仓库地址可能还不够,还需要处理镜像配置:

  1. 通过公司私服下载
    :在 Nexus/Artifactory 中为厂商仓库创建代理,并加入 maven-public 仓库组。Maven 访问 company-public,对应的认证信息也配置在 server.id=company-public 下。
  2. 直接访问厂商仓库
    :将镜像规则改为 <mirrorOf>*,!vendor-releases</mirrorOf>。Maven 会使用厂商的原始 URL,并读取 server.id=vendor-releases 的凭据。这种方式需要开发机和 CI 都能访问厂商仓库,且公司允许直连。

完整解析路径如下图:

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料