serviceloader在jpms中需显式配置uses和provides,模块路径下jar须含meta-inf/services,提供方模块还需exports实现类所在包。

在 Java 模块化系统(JPMS)中,ServiceLoader 仍可使用,但必须显式声明服务提供和使用关系,否则模块无法发现或加载服务实现。核心在于 module-info.java 中的 uses 和 provides ... with 正确配置。
服务接口需在“使用方”模块中声明 uses
调用 ServiceLoader.load(MyService.class) 的模块,必须在 module-info.java 中声明它“使用”该服务接口:
module app.module {
requires service.api;
uses com.example.MyService; // 关键:告诉 JVM 我要加载这个服务
}
注意:requires 是为了编译和运行时访问接口类,uses 才是让 ServiceLoader 能扫描并加载实现类的前提。
服务实现需在“提供方”模块中声明 provides ... with
实现服务接口的模块,不仅要 requires 接口所在模块,还必须用 provides 显式声明自己提供了哪个服务:
module impl.module {
requires service.api;
provides com.example.MyService
with com.example.impl.ConcreteService; // 实现类全限定名
}
一个模块可提供多个服务,也可用同一实现类提供多个接口(若实现多个接口)。
确保模块路径与传统 classpath 分离
模块化环境下,服务 JAR 必须放在模块路径(--module-path 或 -p),不能混入 classpath(-cp)。否则 ServiceLoader 在模块模式下会跳过 classpath 中的 META-INF/services/ 条目。
- 启动命令示例:
java --module-path mods --module app.module/com.example.Main -
mods/目录下应包含service.api.jar、impl.module.jar、app.module.jar等已打包为模块的 JAR - JAR 内部仍需保留
META-INF/services/com.example.MyService文件(内容为实现类全名),这是ServiceLoader的底层查找依据,模块系统只是增强了它的可见性控制
加载时无需改动代码,但要注意模块封装限制
调用方式与非模块化一致:
ServiceLoader<myservice> loader = ServiceLoader.load(MyService.class);
for (MyService service : loader) {
service.doSomething();
}</myservice>
但若服务实现类所在的包未被导出(exports),即使 provides 正确,JVM 也会在实例化时抛 IllegalAccessException。因此提供方模块还需导出含实现类的包:
module impl.module {
requires service.api;
exports com.example.impl; // 让 ServiceLoader 能访问 ConcreteService
provides com.example.MyService with com.example.impl.ConcreteService;
}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











