“我们不是在添加一个新特性,而是在重构 Java 的根基。”
2017 年 9 月,JDK 9 正式发布了,我正忙着把一个老系统往容器化迁移,看到新特性列表时,第一反应是:这回要动真格的了。这是 Java 历史上第一个采用 6 个月快速发布模型的版本,更是首个不被归为长期支持(LTS)的版本。很多人说 JDK 9 是 Java 的转折点,我觉得这句话太轻了。它不只是一个新版本的发布,更像是 Java 这个帝国在重新思考自己的根基。
那时候,我正在做一次系统架构的迁移,尝试将原有的单体应用逐步拆分成服务模块,运维团队也开始推动容器化和最小运行时的概念。当时的团队里,很多人都在说,“原来我们写了这么多年 Java,居然还在用老的技术哲学?”——这句话说实话,还挺有冲击力的。
JSR 379 开门见山:这一版的重点就是给 Java 引入模块系统,并用它把平台本身拆开。换句话说,Java 打算不再把所有东西都扔进一个巨大的类路径里。
这个问题说实话,早已困扰 Java 社区多年,尤其是那些需要处理多个依赖库并且频繁遇到“类路径地狱”的开发者。什么叫类路径地狱?就是你在项目中引用了多个第三方库,这些库中可能有相同类名但不同实现的冲突,设置了错误的类路径,系统就 “抛出异常(或者被搞垮)”,那时我们像在打地鼠一样,一个一个地解决依赖问题。
而 JDK 9 不再回避,它直接切入问题核心:引入了 Java Platform Module System(JPMS),也就是我们常说的 模块化系统。
第一次看到 module-info.java时,当时我挺震惊的,因为Java 一直以“一切皆是类”著称,突然“引用模块”和“声明模块”变成了根本性的设计选择,感觉像是在重新定义编程的边界。
起初,模块化是强制性的,我们必须为项目写 module-info.java。那个阶段真的很痛苦,我带着团队把几十个依赖库逐个检查,发现大多数都没有模块化支持。我们不得不 手动模拟模块划分,或者引用旧式的 --module-path 和 --add-modules 参数。线上环境也不支持,我们的部署流程因此被重新设计。
说实话,JDK 9 让我意识到:Java 不只是语言层面的迭代,更是平台和运行时的革新。它让 jlink 工具开始发挥作用,不再是“拼图”而是“定制化”的运行时生成。就像我后来参与的一个项目,那次用模块化把节点资源占用压低 40%,启动速度快了 20%,是我第一次真切感受到架构调整带来的性能红利。
JDK 9 保留了 JMX、JAXP、JDBC 这些依旧被广泛使用的老牌 API,它们的规约管理权限被转移到了另一个 Umbrella JSR。但像 Applet、CORBA 这类已经脱离时代的技术,则被明确废弃或移除,标志着Oracle正式告别浏览器插件时代。
但有一些功能,JDK 9 明确说“不会在这次实现”。HTTP/2 Client 在 JDK 9 中以孵化器模块出现,直到 JDK 11 才成为标准 API,这为后续的 HTTP/2 支持铺了路。。
有一件事我觉得特别有意思:JSR 379 没有包含 Valhalla、Loom 这些后来频繁出现在 Java 早期规划里的特性和技术。这说明 JCP 在做重大决策时,更注重落地性和可行性。那个阶段,Java 试图“可控化”地演进,而不是所有技术都一股脑地“试验发布”。
说到底,JDK 9 的模块化不只是功能上的改变,它还带来了更加透明的开发流程。我们第一次能看到 OpenJDK 的源码仓库,每周都有 Early-Access Build,模块化生产的 JAR 文件也变得更容易理解和修改。
虽然初期的模块化配置让大家有些手忙脚乱,但随着工具链的成熟,比如 Maven、Gradle、Jib 的模块化支持,慢慢地我们也能适应了。现在回想起来,模块化在 JDK 9 的出现,为后来的容器化和微服务架构打下了基础;至于 GraalVM 这类新技术,则是在后续版本中借助模块化生态逐步成熟。。
JPMS 让依赖管理变得可预期、可控制,也让运行时变得更轻。 从那以后,我们不再只是把一堆 JAR 丢进类路径,而是开始按模块来组织代码、暴露接口、隐藏实现。
对我而言,JDK 9 的模块化是第一次让我认真思考:应用能不能瘦身、依赖能不能理清楚、部署能不能更可控。
后来 JDK 的很多新特性,都是在模块化奠定的基础上慢慢长出来的。它没有改变我每天写业务代码的样子,却改变了我看待整个应用和部署环境的方式。