provides 是 java 9 模块系统中专为 serviceloader 设计的关键字,用于声明本模块为某服务接口提供具体实现,格式为 provides serviceinterface with implclass,仅在运行时被 serviceloader 解析使用。

Java 9 模块系统中,provides 是专为 ServiceLoader 服务发现机制设计的关键字,它不用于导出类、配置变量或提供资源,而是明确声明“本模块为某个服务接口提供了具体实现”。正确使用 provides 的核心在于:接口与实现分离、模块职责清晰、运行时按需加载。
provides 的作用本质
provides ServiceInterface with ImplClass 告诉 JVM:“当其他模块通过 ServiceLoader.load(ServiceInterface.class) 查找服务时,请把 ImplClass 实例化并返回”。它不参与编译期类型检查,也不影响类路径可见性——只在运行时被 ServiceLoader 解析并使用。
- 必须配合
uses ServiceInterface(在调用方模块声明)或直接调用ServiceLoader.load()才能触发查找 - 实现类必须是 public、非 abstract、有无参构造器,且不能是匿名类或非 static 内部类
- 接口本身需由 API 模块定义并
exports,否则其他模块无法引用该类型
四步完成标准服务注册与使用
以发送消息为例,从接口定义到实际调用,需协同多个模块:
-
1. 定义服务接口(API 模块)
- 新建模块
com.example.msg.api,在module-info.java中exports com.example.msg.api - 接口声明为
public interface MessageService { void send(String content); }
2. 编写实现类(实现模块)
- 新建模块
com.example.msg.email,依赖 API 模块:requires com.example.msg.api; - 实现类如
public class EmailServiceImpl implements MessageService,确保可实例化
3. 在实现模块中声明 provides
module com.example.msg.email { requires com.example.msg.api; provides com.example.msg.api.MessageService with com.example.msg.email.EmailServiceImpl; }- 注意:无需
exports实现包——只要ServiceLoader能反射构造,模块可完全封闭
4. 在应用模块中加载并使用
- 应用模块只需
requires com.example.msg.api,不依赖任何实现模块 - 代码中写:
ServiceLoader.load(MessageService.class).findFirst().ifPresent(s -> s.send("Hi"));
常见误区与关键细节
很多开发者误将 provides 当作“导出类”或“配置开关”,导致服务无法加载。以下几点必须确认:
-
META-INF/services/文件已废弃:Java 9+ 不再需要手动维护该目录,provides会自动生成等效元数据 - 模块路径(–module-path)必须包含所有相关模块:若漏掉实现模块,
ServiceLoader查不到任何实现 - 服务接口的全限定名必须一字不差:大小写、包路径、类名全部匹配,否则视为不同接口
- 多个模块提供同一接口时,
ServiceLoader按模块读取顺序返回全部实现,可遍历或按需过滤
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











