5.1 Maven 与持续集成:把作战室搬进流水线 本节摘要:Maven 接入 CI 的三个关键设计是依赖缓存策略、阶段化构建命令与 deploy 权限分离。缓存键按 POM 指纹设计避免全量失效,构建命令按验证深度分层,制品晋级与代码构建的权限分开。本节给出一条完整流水线的配置范例与设计依据。 流水线不是"自动执行的命令行" 把 塞进 CI 的执行框,就宣布"我们持续集成了"——这是最常见的起步姿势,也是最常见的天花板。真正的流水线要回答三个设计问题:每次构建的依赖从哪来(没有缓存策略,每条流水线都在全量下载)、不同事件跑多深的验证(提交跑全量测试还是推送才跑)、谁能把制品放进仓库(deploy 权限无分离,任何一次测试失败的构建都可能污染仓库)。
本节摘要:Maven 接入 CI 的三个关键设计是依赖缓存策略、阶段化构建命令与 deploy 权限分离。缓存键按 POM 指纹设计避免全量失效,构建命令按验证深度分层,制品晋级与代码构建的权限分开。本节给出一条完整流水线的配置范例与设计依据。
把 mvn clean package 塞进 CI 的执行框,就宣布"我们持续集成了"——这是最常见的起步姿势,也是最常见的天花板。真正的流水线要回答三个设计问题:每次构建的依赖从哪来(没有缓存策略,每条流水线都在全量下载)、不同事件跑多深的验证(提交跑全量测试还是推送才跑)、谁能把制品放进仓库(deploy 权限无分离,任何一次测试失败的构建都可能污染仓库)。
risk-engine 的流水线最终形态分四段:提交触发快速验证、合并触发全量验证加质量扫描、主干触发打包与制品晋级、人工确认触发部署。配置范例(以通用 YAML 风格示意,各平台语法细节略有差异):
stages: - 快速验证 # 提交即跑 两分钟内反馈 - 全量验证 # 合并请求时跑 - 质量扫描 # 与全量验证并行 - 制品发布 # 主干保护分支触发 快速验证: script: - mvn -s ci-settings.xml clean test -T 1C -DskipITs # -T 1C 按核数并行构建 缓存命中时两分钟内出结果 cache: key: maven-deps-v1 # 缓存键 见下文设计 paths: - .m2缓存目录/repository/
Maven 在 CI 上的第一性能杠杆不是并行也不是增配机器,是依赖缓存。设计要点在缓存键:按整棵依赖树的指纹做键,依赖没变缓存全命中;用 POM 文件内容计算哈希即可(依赖变更必然反映在 POM 里)。失效策略配合 branch 前缀:主干缓存向下继承,分支缓存向上合并,私服里的新快照靠 -U 或定时刷新任务进缓存。
# 缓存键的进阶设计:POM 哈希加分支维度 cache: key: files: - "全部模块的 POM 清单路径" prefix: "risk-m2" paths: - ".m2/repository/" # 效果:依赖零变更的构建 缓存命中率接近全量 # 拉包时间从四分钟降到十秒内

三个设计原则展开。验证分层:快速段只跑单元测试(-DskipITs 跳过集成测试),全量段补齐集成测试与验收,反馈半径控制在两分钟内——超过这个时长,开发者就切走干别的了,反馈的纠错价值直线下跌。权限分离:CI 的机器身份默认只有仓库读权限,deploy 用的账号凭证只在发布段通过环境变量注入,测试失败的构建在物理上就到不了制品库。制品晋级:一个版本只打包一次,测试、预发、生产用的是同一个制品,环境差异靠运行期配置(第 2.3 节的晚绑定原则),"每个环境重新打包"是配置早绑定的病,必须治。
完整段配置再展开一层,把"全量验证与质量扫描并行、制品发布单独成段"的结构写全:
全量验证: script: - mvn -s ci-settings.xml clean verify # verify 阶段挂载集成测试(第 2.1 节预留的挂载点在这里兑现) 质量扫描: script: - mvn -s ci-settings.xml sonar:sonar # 与全量验证并行跑 互不阻塞(第 5.6 节详解) 制品发布: script: - mvn -s ci-settings.xml deploy -DskipTests # 测试已在前序段跑过 发布段只做装配与上传 # deploy 凭证由受保护的变量注入 平时不可见 rules: - 分支为主干保护分支时触发
注意发布段的 -DskipTests:它不是偷懒,是不重复劳动——测试结论由前序段的质量门禁负责,发布段只信门禁不放行就到不了这里。这与"跳过测试保绿灯"的性质完全不同,区别在于前者有门禁兜底、后者在拆门禁。
CI 专用的 settings 文件(仓库地址、只读账号)与代码同库存放,个人配置一律不进流水线。它长这样:
<!-- ci-settings.xml:流水线的机器身份 极简 --> <settings> <mirrors> <mirror> <id>ci-nexus</id> <mirrorOf>*</mirrorOf> <url>私服地址/repository/risk-group/</url> </mirror> </mirrors> <servers> <server> <id>ci-nexus</id> <username>只读账号</username> <password>环境变量注入或加密串</password> </server> </servers> </settings>
最后补一个阶段演进的常见弯路:团队看到别家的"发布列车"(固定节奏批量发布)很心动,直接照搬节奏却没建第 4 章的制品晋级制度,结果列车每次都带着"没验完的制品"发车。节奏是建制的结果不是原因——先把"一次打包逐环境晋级、每段有门禁"的骨架搭起来,发布节奏只是在这副骨架上选一个发车间隔而已,顺序不能颠倒。
⚠️ 常见坑:CI 机器复用开发者的个人 settings(里面带着个人凭证与镜像配置)。流水线的机器身份必须独立:专用账号、专用 settings 文件进版本库或由配置管理下发,个人配置里的任何私货都不该出现在流水线上——它让"同一份代码两次构建结果不同"的第 3.6 节幽灵故障有了藏身处。
💡 关键直觉:流水线是团队最大的一台"构建机器",它的任何配置漂移都会被乘以每天的构建次数。所以给流水线的每一项配置(settings、JDK 版本、缓存键)都建立版本化与评审,像对待生产代码一样对待它。这条纪律的回报是复利的:配置稳定,缓存键稳定,命中率高,反馈半径才守得住。
流水线立起来了,但它第一天就暴露了新问题:太慢。下一节做慢构建的病理分析——18 分钟到 4 分钟的优化战役全程复盘。