java模块化spi自动加载需同时满足:1.接口模块导出服务接口;2.实现模块声明requires并provides具体实现;3.使用模块requires接口模块且uses该接口;4.运行时所有模块置于--module-path。

Java 模块化系统(JPMS)中实现 SPI 自动加载,核心在于正确声明模块依赖、导出服务接口、提供服务实现,并在 module-info.java 中使用 uses 和 provides ... with 语句。JDK 9+ 的模块系统并未废弃 SPI,而是对其做了模块感知增强——传统 META-INF/services/xxx 仍可工作,但需确保模块路径(--module-path)下模块可见,且服务接口被正确导出和使用。
1. 定义服务接口并导出到其他模块
服务接口需放在一个独立模块中,并显式 export,否则其他模块无法引用该类型:
module com.example.spi.api {
exports com.example.spi;
}
其中 com.example.spi.ServiceInterface 是你的 SPI 接口(如 java.util.ServiceLoader 要加载的类型)。
2. 实现模块声明服务提供者
实现模块必须:
- 声明对服务接口模块的
requires - 用
provides ... with声明自己提供了哪个接口的具体实现类
module com.example.spi.impl {
requires com.example.spi.api;
provides com.example.spi.ServiceInterface
with com.example.spi.impl.ServiceImpl;
}
注意:ServiceImpl 必须是 public 类,且有 public 无参构造器,否则 ServiceLoader 实例化失败。
3. 使用方模块声明服务使用者
调用方模块需:
-
requires服务接口模块(不是实现模块) -
uses该服务接口类型,告知 JVM “我将通过 ServiceLoader 加载它”
module com.example.app {
requires com.example.spi.api;
uses com.example.spi.ServiceInterface;
}
这一步关键:没有 uses,即使 ServiceLoader.load(ServiceInterface.class) 被调用,模块系统也不会自动扫描 classpath/module-path 上的 provides 声明,可能导致加载不到实现。
4. 运行时确保模块路径完整且顺序合理
启动时必须把所有相关模块(API、实现、应用)都放在 --module-path 中,例如:
java --module-path mods/ --module com.example.app/com.example.app.Main
其中 mods/ 目录下包含 com.example.spi.api.jar、com.example.spi.impl.jar、com.example.app.jar。SPI 加载发生在运行时,模块系统会根据 uses 和 provides 关系自动关联实现,无需手动指定 JAR 顺序。
不复杂但容易忽略:模块化 SPI 正常工作的前提是三者(uses、provides、exports)全部到位,缺一不可;传统 classpath 下的 META-INF/services/ 在模块路径中依然有效,但仅限于未命名模块或未加模块声明的 JAR,模块化项目推荐统一使用 provides/uses 方式以获得编译期检查和清晰依赖关系。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











