⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7579489321486893075 「踏浪无痕」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

本文系统梳理 Maven 依赖解析失败的各种场景,覆盖 SNAPSHOT 版本不一致、多模块发布不完整、版本冲突、私服配置等常见问题,提供完整的排查 Checklist 和原理解析。

速查表

遇到问题先对号入座:

现象 可能原因 快速定位
拉到的 SNAPSHOT 不是最新的 本地缓存 / updatePolicy / Nexus metadata 未刷新 场景一
依赖直接找不到 没推完整 / 仓库配置错误 / mirror 拦截 场景二
版本和预期不一致 dependencyManagement 覆盖 / 传递依赖冲突 场景三
本地能跑,别人拉不到 本地仓库残留 / 发布不完整 场景四

场景一:SNAPSHOT 版本不是最新的

现象

你明明推了 1.0.0-SNAPSHOT 的最新代码,同事那边 mvn clean install 之后拉到的还是旧的。

原理

SNAPSHOT 版本在 Nexus 中实际存储为带时间戳的版本,比如 1.0.0-20231203.091530-1。Maven 通过 maven-metadata.xml 来判断哪个时间戳是最新的。

拉不到最新版本,问题可能出在三个环节:

  • 推送端: Nexus 的 metadata 没更新
  • 传输端: Maven 的更新策略没触发检查
  • 接收端: 本地仓库缓存了旧版本

排查步骤

第一步:确认 Nexus 上的真实状态

直接去 Nexus 页面看,或者用 curl 请求:

# 查看 metadata,确认最新时间戳
curl -s "http://your-nexus/repository/snapshots/com/yourcompany/your-artifact/1.0.0-SNAPSHOT/maven-metadata.xml"

关注 <lastUpdated> 和 <snapshotVersions> 里的时间戳,确认是不是你刚推的那个。

第二步:检查本地缓存

本地仓库路径一般是 ~/.m2/repository,找到对应目录:

ls -la ~/.m2/repository/com/yourcompany/your-artifact/1.0.0-SNAPSHOT/

看两个东西:

  • jar 文件的修改时间
  • _remote.repositories 文件内容(记录了从哪个仓库拉的)
  • resolver-status.properties 文件(记录了上次检查远程仓库的时间)

第三步:强制更新

# -U 强制检查远程仓库的 SNAPSHOT 更新
mvn clean install -U

如果 -U 还不行,删掉本地缓存再拉:

rm -rf ~/.m2/repository/com/yourcompany/your-artifact/1.0.0-SNAPSHOT
mvn clean install

根因分析

updatePolicy 配置

在 settings.xml 或 pom.xml 的 repository 配置中:

<repository>
<id>snapshots</id>
<url>http://your-nexus/repository/snapshots</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy> <!-- 关键配置 -->
</snapshots>
</repository>

updatePolicy 可选值:

  • always:每次构建都检查
  • daily:每天第一次构建时检查(默认值)
  • interval:X:每隔 X 分钟检查
  • never:从不检查

如果配的是 daily,那一天内第二次构建就不会去检查远程仓库了。

Nexus metadata 刷新问题

Nexus 3 有时候 metadata 不会自动刷新,可以手动触发:

# Nexus 3 REST API 重建 metadata
curl -X POST -u admin:password "http://your-nexus/service/rest/v1/repositories/snapshots/rebuild-index"

或者在 Nexus 管理界面:Administration → Repository → Repositories → 选择仓库 → Rebuild Index

场景二:依赖完全拉不下来

现象

Could not find artifact com.yourcompany:common-util:jar:1.0.0 in nexus (http://your-nexus/repository/public)

排查步骤

第一步:确认 Nexus 上有没有

直接浏览器访问或 curl:

curl -I "http://your-nexus/repository/releases/com/yourcompany/common-util/1.0.0/common-util-1.0.0.jar"
# 200 表示存在,404 表示不存在

第二步:如果 Nexus 上有,检查本地 settings.xml

常见问题:

  1. mirror 配置拦截了请求
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf> <!-- 这个会拦截所有仓库请求 -->
<url>http://your-nexus/repository/public</url>
</mirror>

mirrorOf=* 意味着所有仓库请求都走这个 mirror,如果你的 artifact 不在这个 mirror 对应的仓库组里,就永远拉不到。

  1. repository 没配或者配错了

确认 settings.xml 里有正确的 repository 配置,并且 <releases> 或 <snapshots> 的 enabled 是 true。

  1. profile 没激活

如果 repository 配置在 profile 里,确认 profile 被激活了:

mvn help:active-profiles

第三步:开启 debug 日志看请求细节

mvn clean install -X 2>&1 | grep -A5 "Downloading from"

这能看到 Maven 实际去哪个 URL 拉的,有没有被 mirror 重定向。

多模块发布不完整

这是二方库最常见的坑。你改了子模块 A,deploy 上去了,但是:

  • 父 POM 没推
  • A 依赖的兄弟模块 B 没推
  • BOM 没推

别人一拉,父 POM 找不到,整个依赖树就断了。

验证方法:

# 检查父 POM 是否存在
curl -I "http://your-nexus/repository/releases/com/yourcompany/parent-pom/1.0.0/parent-pom-1.0.0.pom"

# 检查所有子模块版本是否一致
for module in common-util common-core common-web; do
echo "Checking $module..."
curl -s "http://your-nexus/repository/releases/com/yourcompany/$module/maven-metadata.xml" | grep "<latest>"
done

场景三:版本解析结果和预期不一致

现象

你在 pom.xml 里写的是 1.2.0,但实际用的却是 1.1.0。

排查步骤

第一步:看依赖树

mvn dependency:tree -Dincludes=com.yourcompany:common-util

输出会显示这个依赖是从哪条路径引入的。

第二步:看有效 POM

mvn help:effective-pom | grep -A10 "common-util"

这能看到经过所有继承、导入之后,最终生效的版本是什么。

原理:Maven 版本仲裁规则

当同一个依赖被多条路径引入且版本不同时,Maven 的仲裁规则:

1.最短路径优先(Nearest Definition Wins)

A → B → C → X:1.0
A → D → X:2.0

X:2.0 胜出,因为路径更短(2 层 vs 3 层)

2.路径相同时,先声明优先(First Declaration Wins)

A → B → X:1.0
A → C → X:2.0

如果 B 在 pom.xml 里声明在 C 前面,X:1.0 胜出

3.dependencyManagement 优先级最高

不管路径多长,只要在 dependencyManagement 里锁定了版本,就用这个版本。

常见坑点

1. 父 POM 的 dependencyManagement 覆盖了你的声明

你在自己的 pom.xml 里写:

<dependency>
<groupId>com.yourcompany</groupId>
<artifactId>common-util</artifactId>
<version>2.0.0</version>
</dependency>

但如果父 POM 里有:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.yourcompany</groupId>
<artifactId>common-util</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</dependencyManagement>

最终用的是 1.0.0。

2. import scope 的 BOM 覆盖

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.yourcompany</groupId>
<artifactId>your-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

BOM 里定义的版本会覆盖你自己的声明。多个 BOM 的话,先声明的优先。

3. exclusion 排掉了又被别的路径引入

你排掉了 A 路径的 X,但 B 路径又把 X 带进来了,而且版本还不一样。

用 dependency:tree 的 verbose 模式看全貌:

mvn dependency:tree -Dverbose

会显示被省略的重复依赖和冲突解决过程。

场景四:我本地是好的,别人拉不下来

现象

  • 架构组:”我推了,你看我本地跑得好好的。”
  • 开发组:”我拉不下来,你确定推了?”

真相

本地能跑不代表 Nexus 上有。可能的情况:

  1. deploy 命令执行了,但没成功

网络抖动、权限问题、磁盘满了,deploy 可能中途失败。

  1. 本地仓库有残留

你之前 install 过,本地 ~/.m2/repository 里有这个 jar,所以本地构建能过。但 deploy 的时候可能失败了,Nexus 上根本没有。

  1. 推到了错误的仓库

settings.xml 里配了多个 server,distributionManagement 指向了测试仓库而不是正式仓库。

自检方法

推完之后立即验证:

# 1. 清空本地缓存
rm -rf ~/.m2/repository/com/yourcompany/your-artifact/1.0.0-SNAPSHOT

# 2. 重新拉取,看能不能从 Nexus 拉下来
mvn dependency:get -Dartifact=com.yourcompany:your-artifact:1.0.0-SNAPSHOT

# 3. 或者直接 curl 验证
curl -I "http://your-nexus/repository/snapshots/com/yourcompany/your-artifact/1.0.0-SNAPSHOT/your-artifact-1.0.0-SNAPSHOT.jar"

如果自己都拉不下来,那就是没推成功。

终极排查 Checklist

遇到依赖问题,按这个顺序排查:

1. 先定位问题在哪一端

  • Nexus 上有没有这个 artifact?(curl 或浏览器直接看)
  • 如果没有 → 问题在推送端
  • 如果有 → 问题在拉取端

2. 推送端检查

  • deploy 命令有没有报错?
  • distributionManagement 配置的仓库地址对不对?
  • settings.xml 里的 server 认证信息对不对?
  • 多模块项目:父 POM 和所有子模块都推了吗?

3. 拉取端检查

  • settings.xml 里的 repository 配置对不对?
  • mirror 有没有拦截请求?(mirrorOf 配置)
  • profile 有没有激活?
  • updatePolicy 是什么?(SNAPSHOT 场景)
  • 本地缓存删了吗?

4. 版本冲突检查

  • mvn dependency:tree -Dincludes=xxx 看依赖路径
  • mvn help:effective-pom 看最终生效的版本
  • 父 POM 的 dependencyManagement 有没有覆盖?
  • 有没有 BOM 导入?

常用命令速查

# 强制更新 SNAPSHOT
mvn clean install -U

# 查看依赖树(过滤特定 artifact)
mvn dependency:tree -Dincludes=groupId:artifactId

# 查看依赖树(显示冲突解决过程)
mvn dependency:tree -Dverbose

# 查看有效 POM
mvn help:effective-pom

# 查看有效 settings
mvn help:effective-settings

# 查看激活的 profile
mvn help:active-profiles

# 分析依赖(找出未使用和未声明的依赖)
mvn dependency:analyze

# 下载单个依赖(测试能否拉取)
mvn dependency:get -Dartifact=groupId:artifactId:version

# 清空本地仓库中的特定依赖
rm -rf ~/.m2/repository/com/yourcompany/artifact-name

# 开启 debug 日志
mvn clean install -X

写在最后

Maven 依赖问题的本质,大多是信息不对称——推包的人不知道有没有推成功,拉包的人不知道问题出在哪一环。

解决方案无非三层:

  • 流程规范: deploy 收归 CI,强制校验发布完整性
  • 工具辅助: 自动化的诊断和验证工具
  • 方法沉淀: 把排查套路文档化,减少重复踩坑

本文是第三层。虽然不能根治问题,但至少能让排查过程更高效,少一些扯皮。

文章目录
  1. 1. 速查表
  2. 2. 场景一:SNAPSHOT 版本不是最新的
    1. 2.1. 现象
    2. 2.2. 原理
    3. 2.3. 排查步骤
      1. 2.3.1. 第一步:确认 Nexus 上的真实状态
      2. 2.3.2. 第二步:检查本地缓存
      3. 2.3.3. 第三步:强制更新
    4. 2.4. 根因分析
      1. 2.4.1. updatePolicy 配置
      2. 2.4.2. Nexus metadata 刷新问题
  3. 3. 场景二:依赖完全拉不下来
    1. 3.1. 现象
    2. 3.2. 排查步骤
      1. 3.2.1. 第一步:确认 Nexus 上有没有
      2. 3.2.2. 第二步:如果 Nexus 上有,检查本地 settings.xml
      3. 3.2.3. 第三步:开启 debug 日志看请求细节
    3. 3.3. 多模块发布不完整
  4. 4. 场景三:版本解析结果和预期不一致
    1. 4.1. 现象
    2. 4.2. 排查步骤
      1. 4.2.1. 第一步:看依赖树
      2. 4.2.2. 第二步:看有效 POM
    3. 4.3. 原理:Maven 版本仲裁规则
    4. 4.4. 常见坑点
      1. 4.4.1. 1. 父 POM 的 dependencyManagement 覆盖了你的声明
      2. 4.4.2. 2. import scope 的 BOM 覆盖
      3. 4.4.3. 3. exclusion 排掉了又被别的路径引入
  5. 5. 场景四:我本地是好的,别人拉不下来
    1. 5.1. 现象
    2. 5.2. 真相
    3. 5.3. 自检方法
  6. 6. 终极排查 Checklist
    1. 6.1. 1. 先定位问题在哪一端
    2. 6.2. 2. 推送端检查
    3. 6.3. 3. 拉取端检查
    4. 6.4. 4. 版本冲突检查
  7. 7. 常用命令速查
  8. 8. 写在最后