背景
你是否遇到过"有的 Maven 版本能编译,有的不行"的问题?你是否困惑过为什么配置了仓库却下载不到依赖?本文将深入解析 Maven 的仓库解析机制,通过真实案例帮你彻底搞懂 Maven 的配置原理。
一、核心概念
1.1 什么是 Maven 仓库?
Maven 仓库是依赖构件的存储和分发中心,可以类比为:
| 仓库类型 | 类比 | 说明 |
|---|---|---|
| 本地仓库 | 你的书架 | <code>~/.m2/repository</code>,已下载依赖的缓存 |
| 远程仓库 | 图书馆/书店 | Maven Central、阿里云、公司私服等 |
| 中央仓库 | 国家图书馆 | Maven 官方默认仓库 <code>repo.maven.apache.org</code> |
1.2 什么是镜像?
镜像(Mirror) 是仓库请求的"拦截器"和"重定向器":
原始请求: https://repo.maven.apache.org/maven2/
↓
[镜像拦截检查]
↓
匹配 → 重定向到: https://maven.aliyun.com/repository/public
不匹配 → 继续访问原始 URL
镜像的核心价值:
- 加速下载:使用国内镜像替代国外仓库
- 统一管理:公司内部统一依赖来源,避免外网依赖风险
- 安全控制:拦截不安全的 HTTP 仓库请求
1.3 一个常见的误解
错误认知:Maven 只使用一个仓库下载依赖
正确理解:Maven 维护一个有序的仓库列表,按顺序尝试直到找到依赖
查找依赖: com.example:artifact:1.0.0
↓
检查本地仓库 → 找到了?返回!
↓ 未找到
检查仓库1(nexus-public)→ 找到了?返回!
↓ 未找到
检查仓库2(aliyun-repos)→ 找到了?返回!
↓ 未找到
检查仓库3(Maven Central)→ 找到了?返回!
↓ 都没找到
抛出异常: Could not find artifact...
二、配置文件与优先级
2.1 配置文件位置
| 配置文件 | 位置 | 作用域 | 优先级 |
|---|---|---|---|
| 项目 pom.xml | <code>${project.root}/pom.xml</code> | 当前项目 | ⭐⭐⭐⭐⭐ 最高 |
| 用户 settings.xml | <code>~/.m2/settings.xml</code> | 当前用户所有项目 | ⭐⭐⭐⭐ |
| 全局 settings.xml | <code>${maven.home}/conf/settings.xml</code> | 系统所有用户 | ⭐⭐⭐ |
注意:这里的"优先级"是指配置覆盖优先级,不是依赖查找顺序。依赖查找时,所有来源的仓库会被合并到一个列表中。
2.2 配置文件的影响范围
graph TD
A[Maven 启动] --> B[加载全局 settings.xml]
B --> C[加载用户 settings.xml<br/>覆盖全局配置]
C --> D[加载项目 pom.xml<br/>覆盖 settings 配置]
D --> E[合并所有仓库配置]
E --> F[应用镜像拦截规则]
F --> G[开始解析依赖]
2.3 命令行参数(最高优先级)
# 命令行参数优先级最高,可以覆盖所有配置文件
mvn clean install -Pproduction -DskipTests
完整优先级排序:
命令行参数 > pom.xml > 用户 settings.xml > 全局 settings.xml
三、仓库配置方式
3.1 在 pom.xml 中配置
适用场景:项目特定的仓库,确保团队成员使用一致配置
<project>
...
<repositories>
<!-- 依赖仓库 -->
<repository>
<id>aliyun-public</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled> <!-- 允许下载正式版本 -->
</releases>
<snapshots>
<enabled>false</enabled> <!-- 禁止下载快照版本 -->
</snapshots>
</repository>
</repositories>
<!-- 插件仓库 -->
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<url>https://maven.aliyun.com/repository/public</url>
</pluginRepository>
</pluginRepositories>
</project>
3.2 在 settings.xml 中配置(Profile 方式)
适用场景:用户级别配置,所有项目共享(如公司私服)
<settings>
<profiles>
<profile>
<id>company-nexus</id>
<repositories>
<repository>
<id>nexus-public</id>
<url>http://192.168.1.100:8081/repository/maven-public/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus-public</id>
<url>http://192.168.1.100:8081/repository/maven-public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<!-- ⚠️ 关键:必须激活 Profile 才会生效! -->
<activeProfiles>
<activeProfile>company-nexus</activeProfile>
</activeProfiles>
</settings>
3.3 关键点:Profile 必须激活!
这是一个最容易踩的坑:
| 配置状态 | Profile 生效? | 结果 |
|---|---|---|
| 只定义了 `<profile>` | ❌ 否 | 仓库不会被加入列表 |
| 定义了 `<profile>` + `<activeProfiles>` | ✅ 是 | 仓库生效 |
验证方法:
# 查看有效 POM,看仓库是否被加入
mvn help:effective-pom | grep -A 20 "<repositories>"
3.4 distributionManagement 与 repositories 的区别
<!-- repositories:用于下载依赖 -->
<repositories>
<repository>
<id>nexus-public</id>
<url>http://nexus.example.com/maven-public/</url>
</repository>
</repositories>
<!-- distributionManagement:用于上传发布 -->
<distributionManagement>
<repository>
<id>user-release</id> <!-- 发布正式版 -->
<url>http://nexus.example.com/maven-releases/</url>
</repository>
<snapshotRepository>
<id>user-snapshot</id> <!-- 发布快照版 -->
<url>http://nexus.example.com/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
重要区别:
- `repositories`:下载依赖时使用
- `distributionManagement`:上传构件时使用(`mvn deploy`)
- 两者互不影响,配置的仓库地址通常不同
四、镜像配置详解
4.1 基本镜像配置
<settings>
<profiles>
<profile>
<id>nexusProfile</id>
<repositories>
<repository>
<id>nexus-public</id>
<url>http://192.168.1.100:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus-public</id>
<url>http://192.168.1.100:8081/repository/maven-public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<!-- 激活 Profile -->
<activeProfiles>
<activeProfile>nexusProfile</activeProfile>
</activeProfiles>
</settings>
关键点:Profile 必须通过 `activeProfiles` 激活才会生效!
四、镜像配置
<settings>
<mirrors>
<!-- 阿里云镜像:镜像 Maven 中央仓库 -->
<mirror>
<id>aliyun-central</id>
<mirrorOf>central</mirrorOf>
<name>Aliyun Central Mirror</name>
<url>https://maven.aliyun.com/repository/central</url>
</mirror>
<!-- 阿里云公服:镜像所有公共仓库 -->
<mirror>
<id>aliyun-public</id>
<mirrorOf>*</mirrorOf>
<name>Aliyun Public Mirror</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
</settings>
4.2 mirrorOf 详解:镜像匹配规则
`mirrorOf` 决定了哪些仓库请求会被这个镜像拦截:
| mirrorOf 值 | 含义 | 使用场景 |
|---|---|---|
| `central` | 只拦截 ID 为 `central` 的仓库 | 仅镜像中央仓库 |
| `*` | 拦截所有仓库 | 全局镜像(慎用!) |
| `external:*` | 拦截所有非 `localhost` 的仓库 | 镜像所有远程仓库 |
| `repo1,repo2` | 拦截指定的多个仓库 | 精确控制镜像范围 |
| `*,!repo1` | 拦截除 `repo1` 外的所有仓库 | 排除特定仓库 |
| `external:http:*` | 拦截所有 HTTP 外部仓库 | Maven 3.8+ 的 HTTP 阻断器 |
⚠️ 高级用法示例:
<mirrors>
<!-- 1. 阻止所有不安全的 HTTP 仓库(Maven 3.8+ 默认配置) -->
<mirror>
<id>maven-default-http-blocker</id>
<mirrorOf>external:http:*</mirrorOf>
<url>http://0.0.0.0/</url>
<blocked>true</blocked>
</mirror>
<!-- 2. 公司内部镜像:拦截指定的内部仓库 -->
<mirror>
<id>company-mirror</id>
<mirrorOf>nexus-public,user-release,user-snapshot</mirrorOf>
<url>http://nexus.company.example.com/repository/maven-hub</url>
</mirror>
<!-- 3. 阿里云镜像:拦截所有其他仓库 -->
<mirror>
<id>aliyun-public</id>
<mirrorOf>*</mirrorOf>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
4.3 镜像拦截流程图
请求依赖: org.flywaydb:flyway-core:dm-6.4.4
↓
遍历仓库列表:
↓
1. 尝试 nexus-public (ID: nexus-public)
↓
检查镜像配置
↓
company-mirror 的 mirrorOf 包含 "nexus-public" ✅
↓
重定向到: http://nexus.company.example.com/.../flyway-core-dm-6.4.4.jar
↓
下载成功 ✅
4.4 镜像配置的"陷阱"
陷阱 1:镜像不匹配已定义的仓库
<!-- ❌ 错误配置 -->
<mirrors>
<mirror>
<mirrorOf>nexus-public</mirrorOf> <!-- 想拦截 nexus-public -->
<url>http://nexus.company.example.com/...</url>
</mirror>
</mirrors>
<!-- 但是 Profile 没有激活,nexus-public 根本不存在! -->
<activeProfiles>
<!-- <activeProfile>nexusProfile</activeProfile> 被注释了 -->
</activeProfiles>
结果:镜像永远不会被触发,因为没有仓库的 ID 是 `nexus-public`
*陷阱 2:mirrorOf= 导致过度拦截**
<!-- ⚠️ 谨慎使用 -->
<mirror>
<mirrorOf>*</mirrorOf> <!-- 拦截所有请求 -->
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
结果:所有依赖都从阿里云下载,包括公司内部依赖(如 `dm-6.4.4`)
解决:使用排除语法
<mirrorOf>*,!nexus-public</mirrorOf>
4.5 常见镜像配置推荐
| 场景 | 推荐配置 |
|---|---|
| 纯公共项目 | mirrorOf=central,仅镜像中央仓库 |
| 公司 + 公共混合 | 使用多个 mirror,精确指定 mirrorOf |
| 纯内网环境 | mirrorOf=*,指向公司 Nexus |
| 开发环境 | 禁用 HTTP blocker,允许 HTTP 仓库 |
五、依赖解析完整流程
5.1 Maven 依赖解析的完整生命周期
graph TD
A[执行 mvn 命令] --> B[加载配置文件]
B --> C{settings.xml 中<br/>有 activeProfiles?}
C -->|是| D[激活指定 Profile]
C -->|否| E[跳过 Profile 定义]
D --> F[合并所有仓库配置<br/>pom + settings + parent]
E --> F
F --> G[检查镜像配置]
G --> H{请求的仓库ID<br/>被镜像拦截?}
H -->|是| I[重定向到镜像URL]
H -->|否| J[使用原始仓库URL]
I --> K[尝试下载依赖]
J --> K
K --> L{下载成功?}
L -->|是| M[保存到本地仓库]
L -->|否| N[尝试下一个仓库]
N --> O{还有仓库?}
O -->|是| K
O -->|否| P[抛出异常]
M --> Q[构建继续]
5.2 仓库列表的合并规则
问题:多个配置文件都定义了仓库,最终使用哪些?
答案:所有来源的仓库会被合并成一个列表
最终仓库列表 = 本地仓库
+ settings.xml (激活的 Profile)
+ 项目 pom.xml
+ 父 POM
+ Maven Central
示例:
<!-- settings.xml 中激活的 Profile -->
<profile>
<repositories>
<repository><id>nexus-public</id>...</repository> <!-- ① -->
</repositories>
</profile>
<!-- 项目 pom.xml -->
<repositories>
<repository><id>project-repo</id>...</repository> <!-- ② -->
</repositories>
<!-- 父 POM -->
<repositories>
<repository><id>aliyun-repos</id>...</repository> <!-- ③ -->
</repositories>
合并后的仓库查找顺序:
本地仓库 → ①nexus-public → ②project-repo → ③aliyun-repos → Central
5.3 镜像拦截发生在合并之后
关键理解:镜像拦截的是合并后的仓库列表
步骤1: 合并所有仓库 → [nexus-public, aliyun-repos, central]
步骤2: 应用镜像规则
步骤3: nexus-public 被 company-mirror 拦截 → nexus.company.example.com
步骤4: aliyun-repos 被 aliyun-mirror 拦截 → maven.aliyun.com
步骤5: central 被 aliyun-mirror 拦截 → maven.aliyun.com
六、实战案例分析
6.1 问题现象
环境 A(正常):
- Maven 3.5.4
- 编译成功 ✅
环境 B(失败):
- Maven 3.8.6
- 编译失败 ❌
- 错误:`Could not find org.flywaydb:flyway-core:jar:dm-6.4.4`
6.2 根本原因分析
对比两个环境的 settings.xml
| 配置项 | Maven 3.5.4 | Maven 3.8.6 |
|---|---|---|
| nexusProfile 定义 | ✅ 有 | ✅ 有 |
| activeProfiles 激活 | ✅ 已激活 | ❌ 未激活 |
| HTTP blocker | ❌ 无 | ✅ 有(阻止 HTTP) |
问题链路分析
Maven 3.5.4(成功):
settings.xml:
<activeProfiles>
<activeProfile>nexusProfile</activeProfile> ✅
</activeProfiles>
↓
仓库列表合并:
- nexus-public (来自激活的 Profile) ✅
- aliyun-repos (来自父 POM)
↓
镜像拦截:
- nexus-public 请求 → company-internal-mirror 拦截
- 重定向到: mirror.dimine.net
↓
下载成功: flyway-core:dm-6.4.4 ✅
Maven 3.8.6(失败 - 情况1:无 activeProfiles):
settings.xml:
<activeProfiles>
<!-- nexusProfile 未激活 --> ❌
</activeProfiles>
↓
仓库列表合并:
- nexus-public ❌ (Profile 未激活,不存在)
- aliyun-repos (来自父 POM) ✅
↓
镜像拦截:
- company-internal-mirror 的 mirrorOf 包含 nexus-public
- 但仓库列表中没有 nexus-public,无法拦截 ❌
↓
下载尝试:
- 只尝试 aliyun-repos
- 阿里云没有 dm-6.4.4
↓
下载失败 ❌
Maven 3.8.6(失败 - 情况2:有 HTTP blocker):
settings.xml:
<mirrors>
<mirror>
<mirrorOf>external:http:*</mirrorOf>
<blocked>true</blocked>
</mirror>
</mirrors>
↓
HTTP 仓库请求被阻止:
- nexus-public 使用 HTTP 协议
- 被 maven-default-http-blocker 阻止
↓
下载失败 ❌
6.3 解决方案
方案 1:激活 Profile(推荐)
在 `~/.m2/settings.xml` 中添加:
<activeProfiles>
<activeProfile>nexusProfile</activeProfile>
</activeProfiles>
方案 2:命令行激活(临时)
mvn clean compile -PnexusProfile
方案 3:移除 HTTP Blocker(不推荐)
<!-- 注释掉或删除这个 mirror -->
<!--
<mirror>
<id>maven-default-http-blocker</id>
<mirrorOf>external:http:*</mirrorOf>
...
</mirror>
-->
方案 4:在 HTTP Blocker 中添加例外
<mirror>
<id>maven-default-http-blocker</id>
<mirrorOf>external:http:*,!nexus-public</mirrorOf> <!-- 排除 nexus-public -->
<url>http://0.0.0.0/</url>
<blocked>true</blocked>
</mirror>
6.4 经验总结
| 问题 | 原因 | 解决 |
|---|---|---|
| Profile 定义的仓库不生效 | 未激活 Profile | 添加 `<activeProfiles>` |
| 公司依赖下载不到 | 镜像配置不匹配 | 检查 `mirrorOf` 是否包含仓库 ID |
| HTTP 仓库被阻止 | Maven 3.8+ 的安全策略 | 添加例外或使用 HTTPS |
七、最佳实践与推荐配置
7.1 推荐的 settings.xml 配置(企业环境)
1. 用户执行 mvn 命令
↓
2. 加载 settings.xml 和 pom.xml
↓
3. 激活 activeProfiles 中的 Profile
↓
4. 合并所有仓库配置(pom + profile)
↓
5. 检查 mirror 配置,拦截匹配的仓库
↓
6. 按顺序尝试仓库下载依赖
↓
7. 下载到本地仓库
5.2 仓库优先级
本地仓库 → Profile 中定义的仓库 → pom.xml 中定义的仓库 → 默认中央仓库
被镜像拦截后:
原始仓库请求 → mirror 配置检查 → 镜像仓库
↓ ↓
nexus-public company-internal-mirror 匹配
↓
mirror.dimine.net
六、本次问题案例分析
6.1 问题现象
- Maven 3.5.4:能编译通过
- Maven 3.8.6:编译失败,提示 flyway dm-6.4.4 找不到
6.2 根本原因
settings.xml 差异:
| 配置项 | Maven 3.5.4 | Maven 3.8.6 |
|---|---|---|
| nexusProfile | ✅ 有配置 | ✅ 有配置 |
| activeProfiles | ✅ 激活了 | ❌ 未激活 |
| maven-default-http-blocker | ❌ 无 | ✅ 有(阻止 HTTP) |
6.3 完整链路对比
Maven 3.5.4(正常)
activeProfiles 激活 nexusProfile
↓
nexus-public 仓库被定义(http://192.168.1.100:8081)
↓
company-internal-mirror 拦截(mirrorOf 包含 nexus-public)
↓
重定向到 mirror.dimine.net
↓
成功下载 flyway dm-6.4.4
Maven 3.8.6(失败 - 去掉 HTTP blocker 后)
activeProfiles 未激活 nexusProfile
↓
nexus-public 仓库未被定义
↓
company-internal-mirror 无法拦截(仓库不存在)
↓
只尝试 aliyun-repos(公共仓库没有 dm-6.4.4)
↓
下载失败
Maven 3.8.6(失败 - 有 HTTP blocker)
activeProfiles 未激活 nexusProfile
↓
nexus-public 仓库未被定义
↓
maven-default-http-blocker 阻止所有外部 HTTP
↓
无法访问任何 HTTP 仓库
↓
下载失败
<?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
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<!-- ==================== 本地仓库配置 ==================== -->
<localRepository>${user.home}/.m2/repository</localRepository>
<!-- ==================== 镜像配置 ==================== -->
<mirrors>
<!-- 1. HTTP 阻断器(Maven 3.8+):阻止不安全的 HTTP 仓库 -->
<!-- 如果需要允许特定 HTTP 仓库,使用: external:http:*,!nexus-public -->
<mirror>
<id>maven-default-http-blocker</id>
<mirrorOf>external:http:*,!nexus-public</mirrorOf>
<url>http://0.0.0.0/</url>
<blocked>true</blocked>
</mirror>
<!-- 2. 公司内部镜像:拦截公司仓库请求 -->
<mirror>
<id>company-mirror</id>
<mirrorOf>nexus-public,user-release,user-snapshot</mirrorOf>
<name>Company Internal Mirror</name>
<url>http://nexus.company.example.com/repository/maven-public</url>
</mirror>
<!-- 3. 阿里云镜像:拦截所有其他公共仓库 -->
<mirror>
<id>aliyun-public</id>
<mirrorOf>*</mirrorOf>
<name>Aliyun Public Mirror</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
<!-- ==================== Profile 配置 ==================== -->
<profiles>
<!-- 公司私服 Profile -->
<profile>
<id>company-nexus</id>
<repositories>
<repository>
<id>nexus-public</id>
<name>Company Nexus Public</name>
<url>http://192.168.1.100:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus-public</id>
<name>Company Nexus Public</name>
<url>http://192.168.1.100:8081/repository/maven-public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<!-- ==================== 激活 Profile ==================== -->
<activeProfiles>
<activeProfile>company-nexus</activeProfile>
</activeProfiles>
</settings>
7.2 配置设计思路
镜像拦截优先级(从高到低):
┌─────────────────────────────────────────────────────────┐
│ 1. HTTP Blocker (external:http:*) │
│ ↓ │
│ 2. Company Mirror (nexus-public, user-*) │
│ ↓ │
│ 3. Aliyun Mirror (*) │
└─────────────────────────────────────────────────────────┘
请求流向分析:
- nexus-public 请求 → 被 Company Mirror 拦截 ✅
- user-release 请求 → 被 Company Mirror 拦截 ✅
- 其他 HTTP 请求 → 被 HTTP Blocker 阻止 ❌
- central 请求 → 被 Aliyun Mirror 拦截 ✅
7.3 常用调试命令
# ==================== 诊断类命令 ====================
# 1. 查看有效 POM(包含所有继承和激活的配置)
mvn help:effective-pom
# 2. 查看有效的仓库列表
mvn help:effective-pom | grep -A 50 "<repositories>"
# 3. 查看有效的镜像配置
mvn help:effective-settings | grep -A 30 "<mirrors>"
# 4. 查看依赖树
mvn dependency:tree
# 5. 解析依赖(显示从哪个仓库下载)
mvn dependency:resolve -X
# ==================== 构建类命令 ====================
# 6. 激活指定 Profile 编译
mvn clean compile -Pcompany-nexus
# 7. 强制更新依赖(忽略缓存)
mvn clean compile -U
# 8. 跳过测试编译
mvn clean compile -DskipTests
# 9. 离线模式(使用本地缓存)
mvn clean compile -o
# ==================== 依赖管理类命令 ====================
# 10. 查看依赖的来源
mvn dependency:tree -Dverbose
# 11. 分析依赖冲突
mvn dependency:analyze
# 12. 清理本地仓库中的失败缓存
mvn dependency:purge-local-repository
7.4 开发环境 vs 生产环境配置
开发环境(允许 HTTP,快速迭代)
<!-- settings.xml -->
<mirrors>
<!-- 不阻止 HTTP -->
<!-- <mirror>...</mirror> -->
</mirrors>
<profiles>
<profile>
<id>dev</id>
<repositories>
<repository>
<id>nexus-public</id>
<url>http://nexus.internal.example.com/repository/maven-public/</url>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>dev</activeProfile>
</activeProfiles>
生产环境(仅 HTTPS,安全优先)
<!-- settings.xml -->
<mirrors>
<!-- 阻止所有 HTTP -->
<mirror>
<mirrorOf>external:http:*</mirrorOf>
<url>http://0.0.0.0/</url>
<blocked>true</blocked>
</mirror>
</mirrors>
<profiles>
<profile>
<id>prod</id>
<repositories>
<repository>
<id>nexus-public</id>
<url>https://nexus.internal.example.com/repository/maven-public/</url>
</repository>
</repositories>
</profile>
</profiles>
八、常见问题排查清单
8.1 依赖下载失败
现象:
Could not find artifact com.example:artifact:1.0.0 in ...
排查步骤:
# 步骤 1: 检查仓库是否被激活
mvn help:effective-pom | grep "<id>nexus-public</id>"
# 步骤 2: 检查镜像配置
mvn help:effective-settings | grep -A 5 "<mirrors>"
# 步骤 3: 测试仓库连通性
curl -I http://nexus.internal.example.com/repository/maven-public/
# 步骤 4: 强制更新重试
mvn clean compile -U -DskipTests
# 步骤 5: 清理失败缓存
rm -rf ~/.m2/repository/com/example/
8.2 HTTP 仓库被阻止
现象:
Could not find artifact ... during a previous attempt.
This failure was cached in the local repository
原因: Maven 3.8+ 默认阻止外部 HTTP 仓库
解决方案:
| 方案 | 适用场景 | 配置 |
|---|---|---|
| 方案1 | 临时解决 | 命令行添加 `-Dmaven.wagon.http.ssl.insecure=true` |
| 方案2 | 单仓库例外 | `mirrorOf="external:http:*,!nexus-public"` |
| 方案3 | 升级仓库 | 联系管理员升级为 HTTPS |
| 方案4 | 移除限制 | 删除 `maven-default-http-blocker` mirror(不推荐) |
8.3 Profile 不生效
检查清单:
# ✅ 1. Profile 是否定义?
mvn help:effective-settings | grep "<profile>"
# ✅ 2. Profile 是否激活?
mvn help:effective-settings | grep "<activeProfiles>"
# ✅ 3. 仓库 ID 是否正确?
mvn help:effective-pom | grep "<id>nexus-public</id>"
# ✅ 4. mirrorOf 是否包含仓库 ID?
mvn help:effective-settings | grep "mirrorOf.*nexus-public"
8.4 多个 Maven 版本行为不一致
常见差异:
| 功能 | Maven 3.5.x | Maven 3.8.x | Maven 3.9.x |
|---|---|---|---|
| HTTP Blocker | ❌ 无 | ✅ 默认有 | ✅ 默认有 |
| settings.xml 继承 | 简单 | 更严格 | 更严格 |
| 仓库合并策略 | 基础 | 优化 | 优化 |
迁移建议:
- 备份旧版 `settings.xml`
- 对比两个版本的差异
- 添加必要的 `<activeProfiles>`
- 测试所有仓库连通性
8.5 镜像配置优先级混乱
问题: 配置了多个镜像,不知道实际使用哪个
调试方法:
# 启用调试模式
mvn clean compile -X | grep "Mirror of"
# 输出示例:
# [DEBUG] Mirror of nexus-public: company-mirror
# [DEBUG] Mirror of central: aliyun-public
优先级规则:
- 特定的 `mirrorOf` 优先于通配符
- 配置文件中靠前的 mirror 优先于靠后的
- `!exclusion` 规则优先于包含规则
8.6 完整故障排查流程图
依赖下载失败
↓
检查本地仓库 → 存在且完整?→ 检查冲突版本
↓ 不完整/不存在
检查 effective POM → 仓库列表正确?
↓ 否
修复 activeProfiles / Profile 定义
↓ 是
检查镜像配置 → mirrorOf 匹配仓库 ID?
↓ 否
调整 mirrorOf 配置
↓ 是
检查网络连通性 → 能访问镜像 URL?
↓ 否
修复网络 / 联系管理员
↓ 是
清理缓存重新下载 → mvn clean compile -U
↓
成功 ✅
九、快速参考
9.1 配置文件速查表
| 配置项 | 位置 | 作用 |
|---|---|---|
| `<repositories>` | pom.xml / settings.xml(Profile) | 定义下载仓库 |
| `<distributionManagement>` | pom.xml | 定义发布仓库 |
| `<mirrors>` | settings.xml | 定义镜像拦截规则 |
| `<activeProfiles>` | settings.xml | 激活 Profile |
| `<mirrorOf>` | settings.xml(mirror) | 指定拦截范围 |
9.2 mirrorOf 常用模式速查
| 模式 | 含义 | 使用场景 |
|---|---|---|
| `central` | 仅中央仓库 | 基础加速 |
| `*` | 所有仓库 | 全局镜像(慎用) |
| `*,!repo1` | 除 repo1 外所有 | 精确排除 |
| `repo1,repo2` | 指定仓库 | 精确拦截 |
| `external:*` | 所有外部仓库 | 区分本地 |
| `external:http:*` | 外部 HTTP | 安全拦截 |
9.3 常用 URL
| 仓库 | URL |
|---|---|
| Maven Central | `https://repo.maven.apache.org/maven2/` |
| 阿里云公共 | `https://maven.aliyun.com/repository/public` |
| 阿里云 Central | `https://maven.aliyun.com/repository/central` |
| 阿里云 Spring | `https://maven.aliyun.com/repository/spring` |
十、总结
核心要点回顾
-
Profile 必须激活:定义了 Profile 但不激活,仓库不会生效
```xml
<activeProfiles>
<activeProfile>your-profile-id</activeProfile>
</activeProfiles>
``` -
Mirror 拦截的是仓库 ID:不是 URL,是 `<repository><id>` 的值
```xml
<repository><id>nexus-public</id>...</repository>
<mirror><mirrorOf>nexus-public</mirrorOf>...</mirror>
``` -
多个仓库共存,按顺序尝试:不是只使用一个仓库
```
本地 → Profile → pom → parent → Central
``` -
Maven 3.8+ 默认阻止 HTTP:注意 HTTP blocker 配置
```xml
<mirrorOf>external:http:*,!nexus-public</mirrorOf>
``` -
使用 `-X` 参数调试:看不清发生了什么,就加 `-X`
```bash
mvn clean compile -X
```
配置最佳实践
✅ 推荐做法:
- 在 settings.xml 中配置公司私服 Profile
- 使用 `<activeProfiles>` 自动激活
- 精确指定 `mirrorOf`,避免使用 `*`
- 定期清理本地缓存(`mvn dependency:purge-local-repository`)
- 使用 `-X` 参数调试配置问题
❌ 避免做法:
- 定义 Profile 但不激活
- 使用 `mirrorOf=*` 导致过度拦截
- 混淆 repositories 和 distributionManagement
- 忽略 Maven 版本差异(3.5 vs 3.8 vs 3.9)
参考资料
文末说明: 本文基于真实项目故障案例整理,如有疑问或补充,欢迎交流讨论。
作者: [您的名字]
日期: 2026-03-31
版本: v1.0
# 查看有效 POM(包含所有继承和激活的配置)
mvn help:effective-pom
# 激活指定 Profile 编译
mvn clean compile -PnexusProfile
# 强制更新依赖(忽略缓存)
mvn clean compile -U
# 跳过测试编译
mvn clean compile -DskipTests
# 查看依赖树
mvn dependency:tree
# 解析依赖
mvn dependency:resolve
八、常见问题排查
8.1 依赖下载失败
检查步骤:
- 检查 Profile 是否激活:`mvn help:effective-pom | grep repositories`
- 检查 Mirror 配置:`mvn help:effective-pom | grep -A10 mirrors`
- 检查网络连接:`curl -I <仓库URL>`
- 清理缓存重试:`mvn clean compile -U`
8.2 HTTP 仓库被阻止
现象:
Could not find artifact in ... during a previous attempt.
This failure was cached in the local repository
解决方案:
- 移除 `maven-default-http-blocker` mirror
- 或在 mirrorOf 中添加例外:`external:http:*,!nexus-public`
8.3 Profile 不生效
检查:
- settings.xml 中是否配置了 `activeProfiles`
- 命令行是否使用了 `-P` 参数显式激活
- Profile 是否存在激活条件(如 JDK 版本、系统属性)
九、总结
关键要点
- Profile 必须激活:在 settings.xml 中配置 `activeProfiles` 才会自动生效
- Mirror 只拦截已定义的仓库:仓库不存在则 mirror 无法拦截
- 镜像优先级:mirror 配置会重定向仓库请求到镜像 URL
- 本地仓库缓存:失败的下载会被缓存,需要用 `-U` 强制更新
配置优先级总结
命令行参数 > 用户 settings.xml > 全局 settings.xml > 项目 pom.xml
葫芦客