provides与uses用于建立接口与实现类间的松耦合服务发现关系,解决跨模块行为调用问题;接口模块仅定义契约,实现模块通过provides声明具体实现,消费者模块用uses声明依赖并借助ServiceLoader动态加载。
provides 与 uses 不是用来直接交互“变量”的,而是为接口与其实现类之间建立松耦合的服务发现关系。它解决的是跨模块的行为调用问题,不是状态共享或变量传递。混淆这一点容易误用机制,导致设计失焦。
明确角色分工:接口是契约,实现是插件
服务机制围绕一个公共接口模块展开。这个模块只定义接口(如 PaymentProcessor),不提供任何实现,也不依赖具体业务逻辑。
- 接口模块只需编译通过,无需运行时参与
- 各实现模块(如“支付宝支付模块”“微信支付模块”)各自独立,互不影响
- 消费者模块只依赖接口模块,完全不知道实现模块的存在
在提供方模块中用 provides 声明实现
实现模块的 module-info.java 必须显式声明它提供了哪个接口的哪个实现类:
module com.example.alipay {
requires com.example.payment.api;
provides com.example.payment.api.PaymentProcessor
with com.example.alipay.AlipayProcessor;
}
注意:with 后必须是该模块内已编译、可访问的具体类;一个接口可被多个模块提供不同实现,无冲突。
在消费方模块中用 uses 声明依赖,并用 ServiceLoader 加载
消费者模块不写 new,不硬编码类名,只做两件事:
- 在
module-info.java中声明uses com.example.payment.api.PaymentProcessor; - 在代码中通过
ServiceLoader.load(PaymentProcessor.class)获取所有可用实现
加载后可遍历使用,也可结合模式匹配安全提取特定类型:
ServiceLoader<PaymentProcessor> loader = ServiceLoader.load(PaymentProcessor.class);
for (PaymentProcessor p : loader) {
if (p instanceof AlipayProcessor ap) { // 模式匹配,无需 instanceof + 强转
ap.setMerchantId("M12345");
}
p.process(order);
}
为什么不能用于“变量交互”?替代方案是什么
provides/uses 本身不暴露字段、不同步状态、不传递数据容器。若需跨模块共享配置、上下文或状态:
- 用
ServiceLoader加载的服务实例可自带内部状态(如AlipayProcessor持有apiKey字段) - 多模块共用的运行时配置,应由启动模块统一初始化,通过服务方法参数或上下文对象传入
- 需要响应式状态同步(如 UI 层级联动),应选用框架级机制(如 HarmonyOS 的
@Provide/@Consume或 Spring Context)
强行把变量塞进服务接口,会破坏单一职责,让服务变成“状态桶”,失去可替换性和测试性。

















