6.6 未来战场:模块化、构建加速与云原生 本节摘要:三条演进线正在重塑 Java 构建:JPMS 模块化与 Maven 模块化是互补的两个层面;构建加速从并行走向守护进程与远程缓存;云原生构建把构建本身容器化并面向镜像产物。本节给出三条线的判断框架,以及贯穿始终的一条主线——确定性继续向构建期集中。 三条线,一张时间轴 图:Java 构建的演进时间轴 图:Java 构建的演进时间轴 线一:模块化的两个层面 常被混为一谈的两个"模块化"先拆开:Maven 模块(第 4.1 节)是构建组织层面的——它管源码分仓、依赖编排与 Reactor 顺序,产物之间仍是普通 jar。
本节摘要:三条演进线正在重塑 Java 构建:JPMS 模块化与 Maven 模块化是互补的两个层面;构建加速从并行走向守护进程与远程缓存;云原生构建把构建本身容器化并面向镜像产物。本节给出三条线的判断框架,以及贯穿始终的一条主线——确定性继续向构建期集中。

常被混为一谈的两个"模块化"先拆开:Maven 模块(第 4.1 节)是构建组织层面的——它管源码分仓、依赖编排与 Reactor 顺序,产物之间仍是普通 jar。JPMS 模块(Java 平台模块系统)是运行期层面的——module-info 声明模块的对外 API 与依赖关系,JVM 据此做强封装(第 5.4 节的 InaccessibleObjectException 正是它在执法)。
一个对照实验可以快速体感两者的差别:给 risk-common 加一个 module-info,声明它只对外暴露三个包;然后在 risk-web 里尝试调用一个未暴露包里的类——Maven 构照样通过(Maven 层面依赖是合法的),运行期 JVM 直接拒绝。构建期绿灯、运行期红灯,这正是两个层面各管一段的证据。反过来的例子第 4.1 节早已见过:两个 Maven 模块循环依赖,Reactor 当场报错,而 JPMS 对此毫无意见。
两层互补不互替:Maven 模块回答"代码怎么组织与构建",JPMS 模块回答"运行期谁能访问谁"。实践现状要坦诚:企业应用全面 JPMS 化的比例不高(迁移成本与生态适配是主因),但它的影响力以另一种方式渗透——强封装倒逼反射显形(第 6.4 节原生编译的申报配置与它同源)、运行时影像裁剪(jlink 类方案)依赖它做模块边界。判断框架:新库可以考虑提供 module-info(增量成本小),存量应用按需评估,别为了模块化而模块化——边界清晰的 Maven 模块已经解决了八成问题。
第 5.2 节的优化战役用的是"结构手段"(分层、并行、缓存)。竞赛的下一级是"平台手段":守护进程(常驻构建进程消除冷启动,本地增量构建进入秒级,第 5.2 节提过)与远程构建缓存(模块级产物按输入指纹缓存于服务端,任何机器构建同输入直接命中下载,跳过编译)。远程缓存对多模块工程的放大效应最可观:几十个模块里未变更的模块直接拉缓存产物,全量构建逼近增量构建的耗时。
远程缓存的工作机制值得展开一句,因为它解释了"为什么它敢跳过编译":构建系统把每个模块的输入指纹(源码内容、依赖坐标与版本、编译参数、插件配置的哈希)作为缓存键。指纹相同,输出必然相同,于是直接复用远端产物。任何一项输入变化,指纹变化,缓存自然失效。这套机制的可靠性完全建立在第 5.5 节的三要素上——输入清单里若混进未锁定变量(机器路径、本地时钟、SNAPSHOT),同样输入就会产出不同结果,缓存开始分发错误。所以引入远程缓存的团队,第一件事不是装缓存服务,是跑一遍可复现性自检三问。
两类方案的选择口径也给出:守护进程解决的是"单机交互延迟"(开发者按回车到反馈),远程缓存解决的是"团队重复劳动"(同一份代码被 N 台机器各编译一次)。开发体验优先选前者,CI 成本优先选后者,预算充足则两者叠加——它们不冲突,叠加时守护进程的本地缓存与远程缓存的命中顺序由近及远,天然分层。
第三个演进方向改变的是"构建发生在哪、输出是什么"。第 6.2 节已经预演:构建装进容器(环境一致性由镜像保证,第 5.5 节的 toolchains 问题釜底抽薪)、产物从 jar 变成镜像(构建流水线的后半段围绕镜像的分层、扫描、签名、晋级重组)。云原生构建的完整形态还包括:构建任务跑在编排平台上按需伸缩(构建洪峰不再排队)、镜像的漏洞扫描与准入成为流水线标配(第 3.5 节的漏扫防线延伸到镜像层)。
把云原生构建与传统构建做个对照,演进的逻辑更清楚:
| 维度 | 传统构建 | 云原生构建 |
|---|---|---|
| 构建环境 | 固定物理机或虚机 靠配置管理对齐 | 容器按需起 构建定义即镜像 |
| 弹性 | 排队等固定节点 | 按需伸缩 洪峰不排队 |
| 环境一致性 | toolchains 加 wrapper 锁定 | 镜像即环境 天然一致 |
| 产物形态 | jar 或 war | 镜像或原生二进制为主 |
| 安全准入 | 构建后另跑扫描 | 扫描签名准入内建在流水线 |
Maven 在这条线上的角色反而更纯粹:它专注做好"输入 POM、输出制品"的确定性装配台,容器化与编排交给外围平台。工具边界的清晰化本身就是演进的方向。
Maven 4 的改进方向(consumer POM 分离发布件与消费件、更强化的构建会话、更现代的插件 API)值得跟踪,但不必焦虑——本教程二十余节的作战经验没有一节会被它作废:坐标寻址、传递调解、版本仲裁、生命周期绑定、可复现纪律,这些是机制层的知识,比任何一版工具都活得长。
其中 consumer POM 的思路值得一提,因为它回应的是本教程出现过两次的一个别扭场景:第 4.1 节的内部模块版本管理、第 5.5 节的版本表达式摊平,本质都在解决"发布出去的 POM 该长什么样"与"开发者手写的 POM 长什么样"是两件事的矛盾。发布件想干净(消费者只需要坐标与依赖),开发件想灵活(属性、表达式、内部模块引用)。把两者正式拆开,正是 Maven 4 对多年实践痛点的一次机制层回应——你先在旧版本里理解了矛盾本身,新版本的解法就只是水到渠成的工程收尾。
回望全册那条主线:从第 1 章给构件发身份证,到第 3 章把版本裁决收归建制,到第 5 章锁定一切输入变量,到第 6 章把行为申报提前到构建期——依赖解析作战室打的始终是同一场仗:把运行期的不确定性,一件一件搬到构建期的灯光下。工具会换代,这场仗不会结束。
💡 关键直觉:评估任何新构建技术(新工具、新缓存、新交付形态)就用一个问题:它把哪段不确定性提前锁定了?答得出来的值得投入,答不出来的多半是在制造新的不确定性。
教程到这里收官。从下一次"依赖下载失败"的报错开始,用第 1.4 节的工具打第一炮——作战室已经开门,轮到你值班了。