先看 maven 日志末尾的 could not resolve 或 conflicting versions;挂起超90秒基本是依赖解析卡死,需用 -x 和 dependency:tree -dverbose -dincludes 定位冲突,清理 .lastupdated 文件并统一在 dependencymanagement 中锁定版本。

构建挂起时先看 Maven 日志末尾的 Could not resolve 或 Conflicting versions
持续挂起往往不是卡在编译,而是卡在依赖解析阶段 —— Maven 会反复尝试下载、重试、回退版本,直到超时。日志里如果出现 Could not resolve dependencies for project 后紧跟一堆重试记录,或者出现 Conflicting versions of org.slf4j:slf4j-api 这类提示,基本能锁定是依赖冲突导致的解析阻塞。
关键判断点:挂起前是否出现过 Downloading from central: 循环?是否有 skipping repository 或 Connection refused?这些不是网络问题,而是 Maven 在多个仓库间来回跳转、试图找一个“能凑齐所有传递依赖”的组合,失败后不断重试。
- 不要等 CI 超时再处理 —— 挂起超过 90 秒基本就是解析卡死
- 本地复现时加
-X参数(mvn clean install -X),重点关注[DEBUG] Resolving version range和[DEBUG] Conflict resolution区段 - 跳过测试(
-DskipTests)不能绕过依赖解析,它发生在 compile 阶段之前
mvn dependency:tree -Dverbose 必须配合 -Dincludes 精准定位冲突源头
全量依赖树输出动辄上千行,真正要盯的是那些被多次声明、版本不一致、且被多个模块间接拉入的“枢纽型”依赖(比如 com.google.guava:guava、io.netty:netty-common、org.apache.logging.log4j:log4j-api)。直接扫全量树效率低,容易漏掉 omitted for conflict 后面那个被干掉的版本。
实操建议用 -Dincludes 锁定可疑坐标:
mvn dependency:tree -Dverbose -Dincludes="org.slf4j:slf4j-api"
输出中重点看:
- 哪个路径引入了
1.7.36,哪个路径又引入了2.0.9 - 是否存在
version managed from 1.7.32这类管理覆盖痕迹 - 有没有
omitted for duplicate却没被<dependencymanagement></dependencymanagement>统一收口的情况
强制统一版本必须写进 <dependencymanagement></dependencymanagement>,而不是只在 <dependencies></dependencies> 里声明
很多团队在 <dependencies></dependencies> 里写 <version>2.0.12</version>,以为能覆盖子模块引入的旧版 —— 实际上无效。Maven 的依赖调解规则(nearest definition wins)会让离根 pom 最近的那个声明胜出,而子模块的 pom 往往更近。
正确做法是在根 pom.xml 的 <dependencymanagement></dependencymanagement> 块里显式锁定:
<dependencymanagement><dependencies><dependency><groupid>org.slf4j</groupid><artifactid>slf4j-api</artifactid><version>2.0.12</version></dependency></dependencies></dependencymanagement>
这样所有子模块只要声明该依赖(哪怕不写 <version></version>),都会被强制使用 2.0.12。注意:<dependencymanagement></dependencymanagement> 不会自动引入依赖,只是“管住版本”,对应模块仍需在自己的 <dependencies></dependencies> 中声明。
- 别用
force(Gradle 风格)或resolutionStrategy(非 Maven 原生)—— Maven 没这玩意 - 排除(
<exclusions></exclusions>)只能临时规避,不能替代统一管理;排除太多反而增加维护成本 - 如果用了 BOM(如
spring-boot-dependencies),优先继承它,再在<dependencymanagement></dependencymanagement>里覆盖个别项
CI 环境下必须清理 .lastUpdated 文件,否则挂起会复现
CI 构建节点常复用本地仓库缓存,但一旦某次下载中断,就会留下 xxx.lastUpdated 文件 —— Maven 看到它就拒绝重试,直接卡死在“等待下载完成”。这种状态不会报错,只会无限挂起。
解决方法不是删整个 ~/.m2/repository(太重),而是精准清理:
- Linux/macOS CI 脚本中加一行:
find ~/.m2/repository -name "*.lastUpdated" -delete - Windows CI(PowerShell):
Get-ChildItem -Path "$env:USERPROFILE\.m2\repository" -Recurse -Filter "*.lastUpdated" | Remove-Item -Force - 配合
mvn clean install -U使用,-U强制更新快照,但对.lastUpdated无感,必须先清文件
这个动作成本极低(毫秒级),却能避免 70% 以上的“莫名挂起”。很多团队把 -U 当万能钥匙,其实真正卡住的,往往是那个没人注意的 .lastUpdated。











