Java ServiceLoader在模块路径中注册服务实现的实现方法
来源:17golang原创
时间:2026-09-20 06:09:29 378浏览 收藏
Java ServiceLoader 放到模块路径上使用时,关键不是扫描所有 JAR,而是让消费模块和实现模块在 module-info.java 中分别声明服务关系:消费方写 uses com.example.codec.Codec,提供方写 provides com.example.codec.Codec with com.example.codec.internal.TextCodec。服务接口所在包需要被导出,真正的实现包可以保持封装。
官方资料:https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/ServiceLoader.html
最小闭环是“API 模块导出服务接口、应用模块 uses、插件模块 provides with”,然后用 ServiceLoader.load(Codec.class) 读取实现。
先把三个模块的边界分清
示例用一个 Codec 服务接口连接三个模块:com.example.codec.api 只放接口,com.example.codec.provider 放具体实现,com.example.codec.app 负责加载。这样应用依赖的是接口,而不是实现类。
// 服务接口模块:只公开稳定的 API 类型
module com.example.codec.api {
exports com.example.codec;
}
// 消费模块:声明它可能发现 Codec 实现
module com.example.codec.app {
requires com.example.codec.api;
uses com.example.codec.Codec;
}
// 提供模块:注册实现,但不必 exports 内部包
module com.example.codec.provider {
requires com.example.codec.api;
provides com.example.codec.Codec
with com.example.codec.internal.TextCodec;
}
这里的 uses 是消费方的服务依赖,不会引入某个具体实现;provides ... with ... 则把服务类型和实现类绑定起来。实现包不写 exports 是有意的:调用方只看到 Codec,实现细节仍由提供模块控制。

实现类要满足 ServiceLoader 的实例化规则
实现类可以实现服务接口,也可以提供一个公开的静态 provider() 工厂方法。为了让示例最容易排查,先采用 public 无参构造器,并把实现放在未导出的内部包中。
// 提供模块内部实现:构造器供 ServiceLoader 调用
package com.example.codec.internal;
import com.example.codec.Codec;
public final class TextCodec implements Codec {
public TextCodec() {
// 保留公开无参构造器,避免模块化加载时无法实例化
}
@Override
public String name() {
return "text";
}
}
如果改用 provider 方法,方法必须是 public static、无参数,并返回可赋给服务类型的对象;这时工厂类本身不一定实现服务接口。无论采用哪种方式,提供方都必须列在 provides 的 with 后面,类名必须是全限定名。
应用侧用 ServiceLoader 读取已注册实现
应用模块只引用服务接口。迭代时才会逐个创建实现实例,应用不需要知道提供方的模块名或内部包名。
// 应用入口:只依赖 Codec 服务,不直接依赖 TextCodec
package com.example.codec.app;
import com.example.codec.Codec;
import java.util.ServiceLoader;
public final class Main {
public static void main(String[] args) {
ServiceLoader loader = ServiceLoader.load(Codec.class);
boolean found = false;
for (Codec codec : loader) {
found = true;
System.out.println("发现实现: " + codec.name());
}
if (!found) {
// 空结果说明模块路径或服务声明仍有一处没有接通
System.err.println("未发现 Codec 提供方");
}
}
}
应用模块的 module-info.java 仍然要写 uses,即使代码里已经导入了 ServiceLoader。导入 API 只解决编译期类型依赖,uses 才把服务发现关系写进模块描述符。

用模块路径启动并按证据排查空结果
假设三个模块的源目录分别为 src/com.example.codec.api、src/com.example.codec.provider 和 src/com.example.codec.app,可以把编译输出分开放入 mods:
预期会看到 发现实现: text。若没有任何实现,先确认提供模块的输出目录里存在 module-info.class,再检查 provides 的服务类型是否与 uses 完全一致。若出现访问或实例化错误,重点看服务接口包是否 exports、实现类是否 public,以及构造器或 provider 方法是否符合规则。
还要区分“没有提供方”和“服务类型本身不可用”:前者通常是模块路径或 provides 漏配,后者是服务接口所在模块没有被正确读取。生产环境可以在启动检查中记录发现的实现名称,但不要把内部实现包名当成应用配置契约。
常见边界与速查
| 检查项 | 正确做法 | 典型后果 |
|---|---|---|
| 服务接口包 | API 模块 exports,消费方 requires | 编译或解析阶段无法访问服务类型 |
| 消费声明 | 应用 module-info 写 uses | 模块化应用无法按服务关系发现 |
| 提供声明 | provider 写 provides 接口 with 实现 | 迭代器为空或加载失败 |
| 实现类 | public 无参构造器,或 public static provider() | ServiceConfigurationError |
相关问题
为什么实现包可以不 exports?因为模块系统把服务提供关系单独记录在 provides 中,调用方依赖服务接口,不需要直接访问实现类型。
什么时候还要用 META-INF/services?自动模块或未命名模块仍可用传统 provider-configuration 文件;本文的重点是显式命名模块在模块路径中的 uses/provides 声明。
-
文章 · java教程 | 1星期前 | Java · 异常处理 · 资源管理 · java try-with-resources AutoCloseable close suppressed exception501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
365 收藏
-
346 收藏
-
118 收藏
-
383 收藏
-
306 收藏
-
文章 · java教程 | 9小时前 | Java教程 · CompletableFuture · Java HttpClient Java异步请求超时 CompletableFuture异常处理 HttpTimeoutException sendAsync异常读取278 收藏
-
448 收藏
-
345 收藏
-
文章 · java教程 | 4天前 | 虚拟线程 · Java StructuredTaskScope Java结构化并发 Java子任务取消 StructuredTaskScope结果汇总 Java虚拟线程并发117 收藏
-
149 收藏
-
305 收藏
-
184 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习