背景:一次全量构建喝了杯咖啡还没好
六月我们一个核心服务模块膨胀到 180 个 Maven 模块,mvn install 一次 6 分半。CI 排队严重,开发等得烦躁。我提议评估迁移 Gradle,但团队一半人不乐意——XML 写惯了,怕 DSL 学习成本。于是我做了个对照实验,用同一份源码分别用 Maven 和 Gradle 构建,拿数据说话。
构建性能对比:缓存与并行是关键
同样的 clean build,Maven 6 分 28 秒,Gradle 3 分 12 秒。更关键的是增量构建——改一个模块的测试方法后:
| 场景 | Maven | Gradle |
|---|---|---|
| 全量 clean build | 388s | 192s |
| 改 1 个模块增量 | 351s | 14s |
| 只跑受影响测试 | 全量测试 | 仅相关测试 |
Maven 的增量几乎等于没增量,因为它难以精确判断哪些模块受改动影响。Gradle 的 task 有输入输出指纹,没变化的 task 直接跳过。
DSL 差异:灵活但要克制
Maven 的 pom.xml 是声明式契约,新人上手快;Gradle 的 Groovy/Kotlin DSL 是图灵完备的脚本,能写逻辑:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.junit.jupiter:junit-jupiter'
}
tasks.test {
useJUnitPlatform()
maxParallelForks = Runtime.runtime.availableProcessors()
}
灵活是双刃剑——有人会在 build 脚本里塞业务判断,最后比 pom 还难读。我们的约定是:构建脚本只允许依赖声明和标准化 task 配置,复杂逻辑抽到插件里。
增量构建收益的真实来源
- Task 粒度缓存:编译、测试、打包各为独立 task,输入没变就复用上次的输出。
- 构建缓存(build cache):本地 + CI 共享远程缓存,别人构建过的结果你能直接拉,跨机器也省时间。
- 并行模块:
--parallel按依赖拓扑并行执行无依赖的模块。
迁移决策:不是所有项目都该迁
我们最终只把那几个巨型多模块仓库迁到 Gradle,小项目继续 Maven。理由:模块数少时 Maven 的简洁性更值钱,迁过去收益覆盖不了学习成本。Gradle 的痛点是版本升级偶尔有 breaking change,构建脚本要维护。
留个问题
关于《Maven 到 Gradle 的迁移决策与实践》里这个坑,你当时是怎么处理的?欢迎在评论区聊聊你踩过的类似情况。