java模块化下serviceloader需uses/provides声明、导出接口包、保留meta-inf/services配置,并显式指定类加载器或模块层,缺一不可。

Java 模块化系统(JPMS,Java Platform Module System)中使用 ServiceLoader 组装服务,核心在于接口定义、模块声明、服务提供者注册与加载四者的协同,不能只套用传统 classpath 下的写法。模块化环境下,ServiceLoader 的行为受 module-info.java 控制,类加载器语义也更严格。
明确模块角色:uses / provides 声明是前提
ServiceLoader 在模块化中不是“自动扫描”,而是依赖模块系统的显式契约。必须在对应模块中声明:
-
服务使用者模块(如主应用)需在
module-info.java中写uses com.example.TextProcessor; -
服务提供者模块(如插件 jar)需写
provides com.example.TextProcessor with com.example.impl.UpperCaseProcessor;
这两行声明让 JVM 知道哪些模块“需要服务”、哪些模块“提供实现”,否则ServiceLoader.load()即使路径正确也不会发现实现类。
配置文件仍需保留,但位置和可见性受限
即使有 provides 声明,META-INF/services/com.example.TextProcessor 文件依然必须存在,且内容仍为实现类全限定名(如 com.example.impl.UpperCaseProcessor)。
但注意:
- 该文件必须放在提供者模块的 JAR 根路径下(即
jar -tf provider.jar | grep services能看到) - 提供者模块的
module-info.java必须导出(exports)服务接口所在的包;若接口在独立 SPI 模块中,该模块还需opens或exports给服务加载器所在模块(通常由ServiceLoader.load()所在线程的模块层决定) - 不导出接口包 →
NoClassDefFoundError;不opens包给反射调用 → 实例化失败(因 ServiceLoader 内部用反射调用无参构造)
加载时指定类加载器或模块层更可靠
模块化后,默认 ServiceLoader.load(MyService.class) 使用当前线程上下文类加载器(通常是系统类加载器),但它可能无法访问其他模块的包。推荐方式:
- 若在模块内加载本模块提供的服务,直接用
ServiceLoader.load(MyService.class)即可 - 若跨模块(如主模块加载插件模块的服务),建议显式传入目标模块的类加载器:
ModuleLayer layer = ModuleLayer.boot(); // 或自定义 layer ClassLoader cl = layer.findModule("com.example.provider") .map(Module::getClassLoader) .orElse(Thread.currentThread().getContextClassLoader()); ServiceLoader<myservice> loader = ServiceLoader.load(MyService.class, cl);</myservice>这样避免因模块封装导致资源不可见。
deep-java-review下载Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
运行时需确保模块层完整且可解析
启动时若用 --module-path 启动,所有相关模块(SPI 接口模块、服务实现模块、使用者模块)都必须显式包含,例如:
java --module-path mods/spi.jar:mods/provider.jar:mods/app.jar \
--add-modules ALL-SYSTEM,com.example.app \
-m com.example.app/com.example.app.Main
缺少任一模块 → ServiceLoader 返回空迭代器(hasNext() 为 false),不会报错,容易误判为“没配置”。
基本上就这些。模块化下的 ServiceLoader 不是简单替换 classpath,而是把服务契约从文件约定升级为模块契约,声明、导出、加载三者缺一不可。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










