Java模块能否参照子域名形式分层定义?SPI报错如何解决
分层模块命名可行性
你提到的类似子域名的分层Java模块命名方式是完全合法的。JPMS(Java平台模块系统)对模块名没有“必须对应完全独立代码单元”的强制要求,com.acme.transport、com.acme.transport.water、com.acme.transport.air这类命名符合Java模块命名规范,仅作为模块的唯一标识使用。
需要明确的是:命名上的层级从属关系不会被JPMS自动识别,不存在“父模块自动继承子模块配置、子模块自动获得父模块权限”的规则,所有模块依赖、SPI声明、包导出/开放配置都必须在每个模块的module-info.java中显式定义。
SPI报错根因
你遇到的java.util.ServiceConfigurationError本质是违反了JPMS的SPI校验规则:
JPMS规定,调用
ServiceLoader.load()加载服务的模块,必须在自身的module-info.java中声明对应服务类型的uses指令,该声明不会跨模块传递,和服务接口定义在哪个模块、其他模块有没有声明过uses没有关系。
从错误栈可以看出,加载SelectQueryProvider的调用逻辑实际位于jakarta.nosql.communication.core模块中,但你只在jakarta.nosql.communication.query模块里声明了对应SPI的uses,core模块既没有依赖query模块,也没有声明对应服务的uses,因此触发运行时校验错误。
修复方案
你可以根据代码实际架构选择其中一种方案修复:
- 如果core模块确实需要直接加载query模块定义的SPI:
- 在
jakarta.nosql.communication.core的module-info.java中添加requires jakarta.nosql.communication.query;依赖,保证core模块可以访问到query模块导出的SPI接口; - 补充所有core需要加载的、来自query模块的SPI服务的
uses声明。
- 在
- 如果query是core的独立扩展模块(更符合分层逻辑):
- 将所有query模块专属SPI的
ServiceLoader加载逻辑,从core模块移到jakarta.nosql.communication.query模块内部实现; - 保证声明
uses指令的模块和实际调用ServiceLoader加载服务的模块是同一个,无需修改core模块的配置。
- 将所有query模块专属SPI的
内容的提问来源于stack exchange,提问作者Otávio Santana

