# JavaModule **Repository Path**: sky0535/JavaModule ## Basic Information - **Project Name**: JavaModule - **Description**: Java 平台模块系统 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-19 - **Last Updated**: 2026-08-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Java 平台模块系统(JPMS) ## 项目概述 本项目演示了 **Java 平台模块系统(Java Platform Module System, JPMS)** 的核心特性,基于 **Java 21** 和 **Maven** 构建。项目包含两个模块,通过模块描述文件(`module-info.java`)实现强封装、显式依赖管理以及 **SPI(Service Provider Interface)** 服务加载机制。 ## 项目结构 ``` JavaModule/ ← 项目根目录 ├── .gitignore ├── LICENSE ├── README.md ├── ModuleService/ ← 服务提供模块(module: com.example.ModuleService) │ ├── pom.xml │ └── src/ │ ├── main/java/ │ │ ├── module-info.java ← 模块描述文件 │ │ └── com/example/ │ │ ├── entity/ │ │ │ └── Customer.java ← 实体类(Lombok @Data) │ │ └── service/ │ │ ├── HelloService.java ← SPI 接口定义 │ │ └── demo/ │ │ ├── MorningHello.java ← SPI 实现 1(已注册) │ │ ├── AfternoonHello.java ← SPI 实现 2(未注册) │ │ └── EveningHello.java ← SPI 实现 3(已注册) │ └── test/java/ │ └── simple/test/ │ └── MorningTest.java ← 单元测试(JUnit) └── ModuleTest/ ← 服务消费模块(module: org.simple.ModuleTest) ├── pom.xml └── src/main/java/ ├── module-info.java ← 模块描述文件 └── org/simple/ ├── demo/ │ └── Program.java ← 演示 Customer 实体类 ├── loader/ │ └── ClassLoaderDemo.java ← 类加载器演示 └── spi/ └── ServiceDemo.java ← 通过 ServiceLoader 加载服务 ``` ## JPMS 核心概念 ### 什么是模块? Java 9 引入的模块系统让开发者可以将代码组织为独立、可复用的**模块(Module)**。每个模块通过 `module-info.java` 文件声明: - **模块名称**:唯一标识一个模块 - **依赖关系**:明确声明依赖哪些其他模块(`requires`) - **导出包**:控制哪些包对外开放(`exports`) - **服务提供/消费**:声明 SPI 服务(`provides ... with` / `uses`) ### 模块描述符关键字 | 关键字 | 说明 | 示例 | |---|---|---| | `module` | 定义模块名称 | `module ModuleService { }` | | `requires` | 声明依赖的模块 | `requires java.base;` | | `exports` | 导出包给外部模块 | `exports com.service;` | | `provides ... with` | 声明 SPI 服务及其实现类 | `provides com.example.service.HelloService with ...;` | | `uses` | 声明消费某个 SPI 服务 | `uses com.example.service.HelloService;` | ## 模块详解 ### 1. ModuleService — 服务提供模块 #### `module-info.java` ```java module ModuleService { requires java.base; // 导出包,使其他模块可以访问 exports com.service; exports com.service.demo; // 通过 SPI 机制注册 HelloService 的实现类 provides com.example.service.HelloService with com.example.demo.service.MorningHello, com.example.demo.service.EveningHello; } ``` #### `HelloService.java` — SPI 接口 ```java package com.service; public interface HelloService { String sayHello(String name); } ``` #### 实现类 提供了三个实现类,分别返回不同时间段的问候语: | 实现类 | 包路径 | 问候语格式 | |---|---|---| | `MorningHello` | `com.service.demo` | `早上好:{name}` | | `AfternoonHello` | `com.service.demo` | `下午好:{name}` | | `EveningHello` | `com.service.demo` | `晚上好:{name}` | > **注意**:虽然有三个实现类,但只有 `MorningHello` 和 `EveningHello` 通过 `provides ... with` 注册为 SPI 服务提供者。`AfternoonHello` 未被注册,因此无法通过 `ServiceLoader` 自动发现。 ### 2. ModuleTest — 服务消费模块 #### `module-info.java` ```java module ModuleTest { requires java.base; requires ModuleService; // 依赖 ModuleService 模块 uses com.example.service.HelloService; // 声明使用 HelloService 服务 } ``` #### `ServiceDemo.java` — 通过 ServiceLoader 加载服务 ```java package com.spi; import com.example.service.HelloService; import java.util.ServiceLoader; public class ServiceDemo { public static void main(String[] args) { System.out.println("=================================================="); System.out.println("Java 平台模块系统"); System.out.println("=================================================="); ServiceLoader services = ServiceLoader.load(HelloService.class); for (HelloService service : services) { System.out.println(service.sayHello("李伟峰")); } } } ``` 运行输出示例: ``` ================================================== Java 平台模块系统 ================================================== 早上好:李伟峰 晚上好:李伟峰 ``` ## 关键技术点 ### 1. 强封装(Strong Encapsulation) - 只有通过 `exports` 显式导出的包,其他模块才能访问 - 未导出的包在编译时和运行时都会被拒绝访问 - 这与传统的 `public` 访问修饰符不同——即使类是 `public` 的,如果所在包未导出,外部模块也无法访问 ### 2. 显式依赖(Explicit Dependencies) - `requires` 声明了模块间的显式依赖关系 - 编译器会在编译时验证依赖的模块是否存在于模块路径上 - 所有模块隐式依赖 `java.base`(包含了 `java.lang`、`java.util` 等核心包) ### 3. SPI 服务加载机制 JPMS 增强了 Java 原有的 `ServiceLoader` 机制: - **服务提供方**:通过 `provides` 声明接口与实现类的绑定关系 - **服务消费方**:通过 `uses` 声明需要使用的服务接口 - **运行时**:`ServiceLoader.load()` 自动发现并加载所有已注册的实现 ```mermaid graph LR A[ServiceLoader.load] --> B[HelloService 接口] B --> C[MorningHello] B --> D[EveningHello] B -.-> E[AfternoonHello
未注册,不可发现] ``` ### 4. 包导出粒度控制 | 访问级别 | 说明 | |---|---| | `exports com.service` | 导出包给所有模块 | | `exports com.service to ModuleA` | 限定仅导出给指定模块(qualified export) | ## 构建与运行 ### 前置条件 - JDK 21+ - Maven 3.8+ ### 编译项目 ```bash mvn clean compile ``` ### 运行测试 ```bash mvn test ``` ### 运行 ServiceDemo ```bash mvn exec:java -pl ModuleTest -Dexec.mainClass="com.spi.ServiceDemo" ``` ## 模块系统与传统类路径的区别 | 特性 | 传统类路径(Classpath) | 模块路径(Module Path) | |---|---|---| | 封装控制 | 所有 public 类全局可见 | 只有 exports 的包对外可见 | | 依赖管理 | 隐式(JAR 顺序决定) | 显式(requires 声明) | | 编译时检查 | 无依赖验证 | 编译时验证模块依赖完整性 | | 反射访问 | 完全开放 | 默认受限,需 `opens` 声明 | | 服务发现 | META-INF/services | `provides ... with` 声明式注册 | ## 模块依赖图 ``` ModuleTest │ ├── requires java.base(隐式) │ └── requires ModuleService │ ├── requires java.base(隐式) │ ├── exports com.service ← HelloService 接口 ├── exports com.service.demo ← 实现类 │ └── provides HelloService with MorningHello, EveningHello ``` ## 学习要点 1. **模块描述符是强制的**:每个模块必须在根目录下放置 `module-info.java` 2. **`provides ... with` 替代了传统的 `META-INF/services` 文件**,声明更简洁、更可靠 3. **未通过 `provides` 注册的实现类不会被 `ServiceLoader` 发现**(如 `AfternoonHello`) 4. **`java.base` 是基础模块**,包含 Java 语言核心类,所有模块隐式依赖它 5. **模块名通常与 Maven artifactId 一致**,便于管理