Java模块化是一场持续了十多年的运动,经历了从第三方OSGi标准到官方JPMS(Java平台模块系统)的漫长演进。JDK 9引入的模块化系统不仅是Java语言最大的结构性变化之一,也是JDK自身重构的基础——它将庞大的rt.jar拆分为多个模块,允许应用只包含所需的部分,从而减少内存占用和启动时间。
OSGi是Java模块化的先驱。早在2000年,OSGi联盟就定义了基于Bundle的模块化规范。OSGi Bundle是包含元数据(MANIFEST.MF)的JAR文件,声明了导出的包、导入的包、以及可选的Require-Bundle。OSGi运行在特殊的框架中(如Equinox、Felix),支持模块的动态安装、启动、停止和更新,而无需重启JVM。
参考:https://ltglu.cn/category/sleep-psychology.html
OSGi的强大之处在于其类加载器架构。每个Bundle拥有独立的类加载器,模块之间的类隔离通过不同的类加载器实现。Bundle可以导出特定包,其他Bundle只能访问被导出的包。这种严格的隔离使得同一个JVM中可以运行不同版本的同一库,这在应用服务器和IDE(如Eclipse)中非常有用。
然而,OSGi也有显著的缺点:类加载器架构复杂,调试困难;需要特殊的容器,不能在普通JVM中运行;元数据配置繁琐;动态更新在实际生产中使用较少。这些问题促使Java官方开发自己的模块化系统。
JPMS(Java Platform Module System,又称Project Jigsaw)在JDK 9中正式发布。JPMS的核心是模块描述符(module-info.java),编译后成为module-info.class。模块描述符声明:模块名称、导出的包、依赖的模块、提供的服务等。例如,module com.example.foo { exports com.example.foo.api; requires java.sql; }。
参考:https://ltglu.cn/category/sleep-environment.html
JPMS的类加载器架构比OSGi简单。JDK本身被划分为多个模块(java.base是所有模块的基础),应用模块在启动时被解析,形成一个有向无环图。类加载器仍然存在(Bootstrap、Extension、Application、自定义),但模块系统增强了类加载器的隔离能力。
模块路径是JPMS引入的新概念。传统的类路径(Classpath)是一个扁平的JAR列表,JVM在其中按顺序搜索类。模块路径上的模块支持模块化特性:强封装、可靠配置、以及服务绑定。模块路径上的JAR也可以作为自动模块(放在模块路径但没有module-info的JAR),自动模块名称从JAR文件名推断,并导出所有包。
强封装是JPMS最重要的安全特性。在模块系统中,一个模块只导出明确声明的包,未导出的包即使声明为public,在模块外部也不可见。这打破了长期以来Java中public意味着完全可见的规则,为平台安全提供了新的保障。JDK内部API(如sun.misc.Unsafe)被封装起来,迫使开发者使用官方支持的API。
可靠配置确保模块依赖在启动时被完整解析。如果模块A依赖模块B,但模块B不在模块路径上,JVM在启动时会报错,而不是等到运行时抛出ClassNotFoundException。这种早期错误检测大大提高了应用的可维护性。
服务绑定提供了一种松耦合的依赖注入机制。模块可以使用provides ... with ...声明服务提供者,使用uses声明服务消费者。模块系统在启动时自动将服务消费者绑定到可用的提供者,无需显式配置。
参考:https://ltglu.cn/category/sleep-products.html
JPMS的迁移挑战是巨大的。首先,现有库需要添加module-info.java才能成为显式模块,但这对成千上万的库来说是不现实的。解决方案是自动模块和未命名模块。自动模块位于模块路径上但没有module-info,它导出所有包,并依赖所有其他模块。未命名模块包含类路径上的所有代码,它可以访问所有模块,但模块不能访问未命名模块(除非使用--add-reads)。这种设计允许逐步迁移:先将应用部署到模块路径上,将类路径上的库作为自动模块,然后逐步为库添加模块描述符。
JDK自身的模块化是JPMS的最大成功。JDK 9将rt.jar(约60MB)拆分为大约100个模块。这意味着应用可以只包含所需的模块,使用jlink工具创建自定义的JRE镜像。对于微服务、容器化应用和嵌入式系统,这可以显著减少内存占用和镜像大小。
多版本JAR是JPMS的辅助特性,允许在同一个JAR中包含针对不同Java版本的类。这在模块化迁移中很有用——你可以为Java 9+提供module-info.class,同时保持对Java 8的兼容性。
模块化与IDE的集成是JPMS普及的关键。IntelliJ IDEA、Eclipse、NetBeans都支持创建和管理模块化项目,包括module-info.java的编写、模块路径的配置、以及模块图的可视化。构建工具Maven和Gradle也支持模块路径,但配置略有不同。
JPMS并非没有批评者。有人认为JPMS过于复杂,增加了学习成本;有人认为它的服务绑定不如Spring或Guice强大;还有人认为模块化带来的好处不足以抵消迁移成本。这些批评有一定道理,但JPMS的主要价值在于JDK自身的模块化和对大型应用的结构化支持,而不是作为通用的依赖注入框架。
参考:https://ltglu.cn
在实践中,是否使用JPMS取决于项目规模。对于小型应用,类路径和传统JAR已经足够。对于大型、长期维护的企业应用,模块化可以显著改善架构的可维护性。对于构建自定义JRE镜像的容器应用,JPMS几乎是必需的。