JPMS:Java 平台模块系统(Java Platform Module System) 作者:马育民 • 2026-10-12 09:50 • 阅读:10000 **提示:**目前maven上的库,大部分不支持JPMS # 介绍 从 **Java 9** 正式引入,Java Platform Module System,缩写:**JPMS**,也叫 **Jigsaw 项目** ### 目标 **把庞大的 JDK 拆成模块、强封装、显式依赖、控制访问边界**,解决传统JAR包“类路径地狱(classpath hell)”、包随意访问、无法声明依赖等问题。 # 传统ClassPath的痛点 1. **所有类都在扁平的classpath**,没有版本隔离,多个版本jar冲突直接覆盖 2. **包访问权限不可控**:只要在classpath,就能反射访问其他包非 `public` 类 3. **无法显式声明依赖**,运行时报类缺失,编译期不知道需要哪些jar 4. JDK本身是一个巨大整体,哪怕只写简单程序也要加载整个 `rt.jar` ### 例子 1. 所有类都在扁平classpath,没有版本隔离,多版本jar冲突直接覆盖 👉 **业务/第三方jar(程序员项目里引入的jar包)** classpath是一个扁平列表,例如: `lib/guava-18.jar, lib/guava-21.jar` 两个版本同时放进去,**类加载顺序决定哪个生效,另一个版本的类直接被“掩盖”**,也就是经典Jar Hell。 2. 包访问权限不可控:只要在classpath,就能反射访问其他包非public类 👉 **业务jar、第三方jar、JDK rt.jar,全部都有这个问题** 只要类在classpath,就算是包私有、private类,代码都可以通过反射强行访问。 - 业务库:可以直接反射调用第三方库内部实现类(破坏封装,升级容易崩) - JDK:可以反射访问`sun.misc.Unsafe`这类JDK内部API。Java9就是**靠模块化把JDK内部包封闭起来,禁止外部模块访问。** 3. 无法显式声明依赖,运行时报类缺失,编译期不知道需要哪些jar 👉 **程序员应用项目(业务+第三方jar)** 传统Maven/Gradle是**构建工具层面的依赖**,**不是Java语言层面的依赖**。 - Maven只是下载jar; - JVM本身完全不知道A.jar依赖B.jar; 你丢少一个jar,编译可能过(IDE带依赖),打包运行才抛`ClassNotFoundException`。 > JDK内部类之间的依赖是固定写死在rt.jar里,不存在这个痛点。 4. JDK本身是一个巨大整体,哪怕简单程序也要加载整个rt.jar 👉 **纯指JDK自带类库(Java8及更早)** Java8及以前,全部JDK核心类都打包在`rt.jar`,哪怕你只写一行`System.out.println()`,JDK也带着整个rt.jar。JPMS把rt.jar拆成几十个小JDK模块(java.base、java.sql等),支持jlink裁剪,只打包需要的模块。 ### 解决 JPMS引入**模块(Module)**,每个模块有独立的名字、依赖列表、导出包,实现**强封装+依赖声明**。 JPMS的设计思路: 1. 先**把JDK拆成模块**,解决JDK单体庞大、内部API随意被反射调用; 2. 同时**开放模块能力给应用开发者**,让我们自己写的代码、打包的jar,也能用上模块:显式依赖、包导出、强封装。 # 概念 ### 1. 模块 module 模块是一组包的集合,**一个模块 = 多个package + 模块描述文件`module-info.java`**。 **注意:** **一个jar包可以是一个模块(模块jar)**,也可以是普通jar(类路径jar,在模块化体系里叫**未命名模块 unnamed module**) ### 2. module-info.java(模块描述符,核心文件) 放在**源码根目录**,和包目录同级,**必须在源码根下**,不能放到package里面。 ```java // 声明模块名称 module com.example.myapp { // 1. 依赖其他模块 requires java.sql; requires transitive com.example.common; // 传递依赖 // 2. 导出包:允许外部模块访问这个包下public类型 exports com.example.myapp.service; // 3. 只导出给指定模块(限定导出,最严格) exports com.example.myapp.internal.util to com.example.web; // 4. 开放包:允许反射访问(框架Spring/Hibernate常用) opens com.example.myapp.entity; opens com.example.myapp.controller to com.example.web; // 5. 提供服务:SPI,本模块实现某个服务接口 provides com.example.spi.DemoService with com.example.myapp.impl.DemoServiceImpl; // 6. 使用服务:消费SPI接口 uses com.example.spi.DemoService; } ``` #### 解释 1. **module**:定义模块,模块名推荐反向域名,不能和包名强制一致,但建议保持一致 2. **requires**:声明**编译期+运行期**依赖另一个模块 - `requires transitive`:传递依赖;A requires transitive B → 依赖A的模块自动获得B,不用再写requires B 3. **exports <包名>**:对外暴露**public类、接口**;**不导出的包,外部模块完全不可见(编译报错)**,即使是public类 4. **exports ... to 模块列表**:**限定导出**,只允许指定模块访问该包 5. **opens <包名>**:允许**反射访问**包里面所有类(包括非public),编译期仍然不能直接访问源码;ORM、IOC框架必备 6. **opens ... to**:限定开放,仅指定模块可反射访问 7. **provides / uses**:JPMS内置SPI服务机制,替代旧的`META-INF/services` > ✅ JPMS核心封装规则: > **模块内不export的包,其他模块连编译都无法访问;反射也不行,除非opens。** > 对比旧版本:只要类在classpath,就能反射访问任意包public类。 # 模块分类 1. **系统模块**:JDK自带模块,`java.base`是**基础核心模块,所有模块隐式依赖java.base,不需要写requires** ``` java.base 基础:Object, String,集合,IO java.sql JDBC java.xml XML解析 java.net.http HttpClient jdk.httpserver 内置http服务器 ``` 2. **应用模块**:自己写的业务模块 3. **自动模块 Automatic Module** 普通第三方jar(没有module-info.java)放到`module-path`下,会被自动当成模块: - 模块名默认从jar文件名推断(去掉版本号) - **自动模块隐式导出所有包**(兼容旧代码) > 很多老框架(Spring5、MyBatis早期版本)作为自动模块使用 4. **未命名模块 Unnamed Module** jar放在传统`-cp / -classpath`,全部归到**未命名模块**。 规则: - 未命名模块**可以读取所有模块** - 命名模块**不能requires未命名模块**(单向隔离,这是模块化重要限制) 开发建议:新项目用`--module-path`;老项目迁移时,第三方jar一般作为自动模块。 # 模块化的限制与坑点 1. **包冲突**:同一个包名不能出现在**同一个模块路径**下的多个模块中(命名模块不允许包分裂split package) > 传统classpath允许多个jar有相同包;模块化命名模块禁止,自动模块有部分宽松规则 2. **未命名模块单向访问**:命名模块不能requires未命名模块;未命名模块能读取所有命名模块。 3. `java.base`不能修改,不能requires其他系统基础模块。 4. 很多反射框架需要`opens`,否则会报`IllegalAccessException`,Spring、Hibernate必须配置opens。 5. 模块名不能带版本号;版本号放在jar文件名,不在module-info里定义。 6. 不破坏向下兼容:**你可以完全不写module-info.java,继续用classpath老方式**,模块化是可选特性。 # 适用场景 & 不适用场景 ### 适合: - 大型多模块项目,组件拆分、强边界管控 - 做SDK、公共组件库,控制内部类不被外部调用 - 精简JDK(jlink打包,只保留需要模块,生成最小JRE) ### 不推荐: - 简单小Demo、单jar小工具,增加复杂度 - 大量老旧第三方库,需要大量opens配置(SpringBoot项目默认**不启用JPMS**,SpringBoot用classpath为主) 原文出处:/show_1GW4DPozJkTW.html