ServiceLoader 在模块化环境拿不到新模块服务实现,根本原因是模块系统未将其实现识别为合法提供者;需确保 uses/provides 声明严格对齐、模块已正确解析并加入 ModuleLayer、实现类满足 public 无参构造等契约,且接口与实现类均被正确导出和加载。

模块化环境下 ServiceLoader 拿不到新模块的服务实现,核心问题不是“没加载”,而是“没看见”——模块系统根本没把那个服务当合法提供者。关键在 uses/provides 声明是否对齐、是否生效、是否被正确解析。
模块契约必须双向显式声明
Java 模块系统不扫描类路径,只认 module-info.java 里的白名单:
- 消费者模块(比如你的主应用或插件管理器)必须写:
uses com.example.MyService; - 提供者模块(新加入的插件 JAR)必须写:
provides com.example.MyService with com.example.NewServiceImpl; - 两者接口全限定名必须完全一致,大小写、包路径一个字符都不能错
- 如果提供者模块还依赖其他模块(如 common-utils),需在
requires中补全,否则provides声明会被忽略
模块层(ModuleLayer)加载时要传对配置
用 ServiceLoader.load(ModuleLayer, Class) 时,ServiceLoader 只查该 layer 里已解析并链接好的模块:
- 确保新模块已通过
Configuration.resolveAndBind()加入 layer 配置,不能只丢进模块路径就完事 - 调用
ServiceLoader.load(layer, MyService.class)前,layer 必须已完成defineModulesWithOneLoader() - 若用自定义
ModuleFinder加载新模块,要确认 finder 能找到它——常见错误是路径写错、JAR 未解压、或被 IDE 缓存屏蔽
检查实现类是否满足基础契约
即使模块声明全对,实现类本身不合格也会静默失败(返回空迭代器):
- 类必须是
public顶层类,不能是内部类、匿名类或默认访问权限类 - 必须有
public MyServiceImpl()无参构造器(Lombok 的@RequiredArgsConstructor不行) - 类名必须与
provides ... with xxx后面写的全限定名严格一致 - 该类所在包必须由提供者模块
exports出去,否则消费者看不到这个类型
验证是否真被加载的实操方法
别只看迭代器 size(),用下面方式确认:
- 打印
layer.modules().stream().map(Module::getName).collect(toList()),看新模块名是否在列表中 - 对新模块调用
module.getDescriptor().provides(),确认返回值包含你的服务接口 - 在消费者模块里加一句:
System.out.println(MyService.class.getModule());,看它归属哪个模块——如果显示unnamed module,说明接口没走模块化加载,整个链条就断了

















