一次 Maven 版本升级引发的构建失败:传递依赖与 reactor 排序
项目版本号从 1.0.19 升到 1.0.20,全量构建,结果红了。报错只有一句,落在 nebula-client 模块:
1 | [ERROR] Could not resolve dependencies for ...:nebula-client:jar:1.0.20-SNAPSHOT |
这个报错最迷惑的地方在于:nebula-proto-conf 明明是本项目自己的一个模块,源码就在同一个工程里。自己的模块,为什么会找不到,还要跑去私有仓库拉?
更离谱的是,报错的 nebula-client 的 pom 里,压根没有 nebula-proto-conf 的依赖。
先看工程结构
先把这个项目长什么样讲清楚,不然后面会一头雾水。
1 | nebula-ad(广告管理平台 · Maven 多模块) |
项目里的版本号统一在根目录用 dependencyManagement 管理。坑就在这儿,后面细说。
排查依赖
排查 Maven 依赖,一般先看它是直接依赖还是传递依赖。
翻了下 nebula-client 的 pom,没有直接依赖 nebula-proto-conf。那多半是某个直接依赖把它带进来的。接下来就是找,到底是哪个包引入了 nebula-proto-conf:
1 | mvn dependency:tree -Dincludes=com.nebula.proto:nebula-proto-conf |
输出
1 | [INFO] com.nebula:nebula-client:jar:1.0.20-SNAPSHOT |
当然,也可以在 IDEA 的 Maven 工具窗口里点 Show Dependencies,直接搜 JAR 名称定位。
依赖路径是:nebula-client -> resume-facade -> nebula-proto-conf
看到这里,疑点就来了:1.0.20 是这次刚改的版本号,resume-facade 怎么可能声明的是 1.0.20? 去翻 resume-facade 的实际依赖,发现它写的是 1.0.8。
问题拼图
到这里,事情基本清楚了:
resume-facade传递依赖nebula-proto-conf,它声明的是 1.0.8;- 根 pom 的
dependencyManagement把版本统一改写成了 1.0.20; nebula-client对nebula-proto-conf只是传递依赖,pom 里没有直接声明;- Maven 在 reactor 拓扑排序时,识别不到这条依赖边;
- 于是构建顺序里,
nebula-client被排到了nebula-proto-conf之前——后者还没产出 1.0.20 的 jar; - 本地没有、去私服也拉不到,失败被缓存,后续模块全部 SKIPPED。
单独看哪一步都不是大问题,凑一块才有问题。版本升级只是那个把雷点着的火苗。
Maven 决定模块构建顺序时,主要认的是「pom 里直接声明的依赖」。它会在依赖里找 groupId:artifactId 能对上 reactor 内模块的,据此建一条「A 依赖 B,所以 B 排前面」的边。
但 client -> proto-conf 这条边,是 facade 在传递依赖里带进来的,排序阶段根本不可见。于是 Maven 只能按模块声明顺序排,把 client 排到了 proto-conf 前面。
解决方案
解决问题有两个选择:
nebula-client显式声明nebula-proto-conf的依赖;- 调整项目
modules中的顺序,把nebula-proto-conf放到前面。
我选择了第一种。
显式声明后,Maven 就能识别出 nebula-client -> nebula-proto-conf 这条直接的依赖边,拓扑排序会自动把 nebula-proto-conf 排到前面,直接用自己构造的结果,问题根治。
调整 modules 顺序虽然这一次也能跑通,但它把依赖关系藏进了模块列表的顺序里,是个脆弱契约。以后谁重排一次 modules,或者加个新模块,又会在莫名其妙的地方复现。








