Java ModuleDescriptor.Provides 怎样读取服务提供者的类名
先从 ModuleDescriptor.provides() 取得服务提供声明集合,再对每个 ModuleDescriptor.Provides 调用 service() 和 providers()。前者是服务类型的二进制名称,后者是提供者或提供者工厂的二进制类名列表,返回的是字符串而不是服务实例。
这适合做模块声明清单、部署诊断和报告展示。名单描述“模块声明了什么”,不能据此断言类已经加载、服务已经成功创建或业务调用已经通过。下面先给可复制的最小示例,再说明运行时入口和记录范围。
Java 官方地址:https://dev.java/
最小写法:遍历 provides 声明
相关模块 API 从 Java 9 起提供。为便于单独理解,示例用构建器创建一个内存中的描述符。示例名称是教学数据,不要求真实提供者类存在,也没有在本机执行来证明服务可用。
import java.lang.module.ModuleDescriptor;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class ProvidesNames {
public static void main(String[] args) {
// 这里只构造声明元数据,不加载或实例化这些教学类名。
ModuleDescriptor descriptor = ModuleDescriptor
.newModule("demo.reports")
.provides("demo.spi.ReportService",
List.of("demo.impl.TextReport", "demo.impl.ReportFactory"))
.build();
List declarations =
new ArrayList(descriptor.provides());
declarations.sort(Comparator.comparing(
ModuleDescriptor.Provides::service));
for (ModuleDescriptor.Provides entry : declarations) {
System.out.println("服务类型:" + entry.service());
for (String providerName : entry.providers()) {
System.out.println("声明的提供者或工厂:" + providerName);
}
}
}
}
provides() 是一组服务声明,不能直接当成类名集合;真正的名称列表位于每个声明的 providers() 中。示例按服务名称排序,目的是让报告排列稳定,而不是设置服务选择优先级。

给名称列表保留模块与服务上下文
生产报告中只保存一个类名不够:它属于哪个模块,为哪个服务类型声明,也应一起记录。可把一条报告行理解为模块名、服务类型名和提供者名称列表三个字段;不要把多个模块同名服务的声明混成一条没有来源的字符串。
例如上面的 demo.impl.ReportFactory 只是放入描述符的名称。API 文档允许该列表表示提供者或提供者工厂,因此报告字段宜叫“声明的提供者或工厂”,不能无条件标成“接口实现类”。
providers() 的返回列表非空且不可修改。如果需要排序展示,先创建副本再排序,保留原声明列表,避免对不可修改列表调用 sort() 导致异常。
// entry 是上文循环中的一项服务声明,只改展示副本。 ListdisplayedNames = new ArrayList(entry.providers()); displayedNames.sort(String::compareTo); String moduleName = descriptor.name(); String serviceName = entry.service(); // 写日志时应分别保存这些字段,不把排序理解为加载优先级。

只读取元数据,不把类名当作可用实例
把描述符里的名字读出来,与实际发现并创建服务,是两种不同操作。前例只构造元数据,打印名称不会自动检查这些教学类的构造方法或工厂方法。将这段代码用于清单时,不必为了“证明类存在”追加反射实例化。
同样,描述符的 provides 声明不替代模块图、类加载器和实际服务发现上下文。若读者要解决的是服务加载失败,应另行调查发现和实例化阶段;本文的结果只能作为声明依据,不能作为可用性证明。
用于生产清单时,保持工具只读元数据即可,不需要为了读名称额外开放包或修改模块可读关系。不要根据名单自动执行未知构造器,也不要以修改 opens 作为枚举字符串的必要步骤。
从运行时模块取值时处理空描述符
已有某个已知业务类时,可以通过它所属的运行时模块取得描述符。关键是先处理未命名模块:Module.getDescriptor() 在这种情况下返回 null。代码位于 classpath 时,不应把没有描述符误判为“模块声明了零个服务”。
// 放在上文 main 中使用;实际项目可替换为已知业务类。
Module module = ProvidesNames.class.getModule();
ModuleDescriptor runtimeDescriptor = module.getDescriptor();
if (runtimeDescriptor == null) {
System.out.println("当前类属于未命名模块,没有模块描述符");
} else {
// 空集合与没有描述符是两种不同状态。
List entries =
new ArrayList(runtimeDescriptor.provides());
entries.sort(Comparator.comparing(ModuleDescriptor.Provides::service));
for (ModuleDescriptor.Provides entry : entries) {
System.out.println(runtimeDescriptor.name() + " | "
+ entry.service() + " | " + entry.providers());
}
}
该片段只观察这个类所属的模块,不扫描所有模块。若读到的集合为空,只能说这个描述符没有提供声明;不能据此推导整个进程没有服务,也不能拿它否定其他模块中的声明。
记录与交付时保留哪些信息
| 数据或状态 | 准确含义 | 记录建议 |
|---|---|---|
| descriptor.name() | 被读取描述符的模块名 | 作为报告来源 |
| entry.service() | 服务类型的二进制名称 | 与提供者名单一起保存 |
| entry.providers() | 提供者或工厂的二进制类名列表 | 保留原值,展示排序使用副本 |
| 描述符为 null | 当前运行时模块未命名 | 单独标记,不写作空服务清单 |
| provides 集合为空 | 此描述符没有提供声明 | 保留模块上下文 |
交付这类诊断结果时,应同时记录从哪个模块和哪份应用构建取得描述符,以及这是“声明清单”。不要写成“服务健康检查全部通过”。类名是二进制名称,记录时保持原字符串,不擅自把其中的美元符号替换为点号,也不改写成文件系统路径。
为什么不能直接修改 providers() 返回的列表?
该 API 返回不可修改列表。需要过滤或排序时新建副本,不把元数据对象当成可变配置。
读取到的名称一定是实现接口的类吗?
不能这样假设。文档明确包含提供者工厂名称;元数据读取本身也不验证实际创建能否成功。
这个方法会实例化服务吗?
不会。本文调用描述符的名称访问方法,只读取声明字段;实际服务发现与创建不在该片段内。
Java 25 flexible constructor bodies 怎样在 super 前准备参数
- 上一篇
- Java 25 flexible constructor bodies 怎样在 super 前准备参数
- 下一篇
- Java StructuredTaskScope 怎样收集部分成功结果
-
- 文章 · java教程 | 24分钟前 | Java · 语言特性 · java super Java 25 Flexible Constructor Bodies
- Java 25 flexible constructor bodies 怎样在 super 前准备参数
- 212浏览 收藏
-
- 文章 · java教程 | 25分钟前 | Java 25 · java Java 25 Compact Source Files instance main
- Java 25 简洁源文件与实例 main 的启动入口配置方法
- 106浏览 收藏
-
- 文章 · java教程 | 1天前 |
- Java String.indent 为什么会自动补换行符
- 173浏览 收藏
-
- 文章 · java教程 | 1天前 |
- Java CompactNumberFormat 怎样按 Locale 格式化大数字
- 266浏览 收藏
-
- 文章 · java教程 | 1天前 |
- Java String.lines 怎样统一处理 CRLF 和 LF
- 372浏览 收藏
-
- 文章 · java教程 | 1天前 |
- Java MappedByteBuffer load 和 isLoaded 有什么区别
- 243浏览 收藏
-
- 文章 · java教程 | 1天前 |
- Java MappedByteBuffer.force 怎样只刷指定文件范围
- 330浏览 收藏
-
- 文章 · java教程 | 1天前 | Java · nio · java Slice ByteBuffer duplicate
- Java ByteBuffer duplicate 和 slice 修改数据有什么区别
- 498浏览 收藏
-
- 文章 · java教程 | 1天前 |
- Java ByteBuffer.alignedSlice 怎样按内存对齐创建切片
- 322浏览 收藏
-
- 文章 · java教程 | 1天前 |
- Java UserPrincipalLookupService 查找用户组失败怎么办
- 462浏览 收藏
-
- 文章 · java教程 | 1天前 |
- Java AclFileAttributeView 怎样读取文件的 ACL 条目
- 110浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 912次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 919次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1003次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 995次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 815次使用
-
- Java 时间格式兼容多个输入格式的方法
- 2026-10-18 501浏览
-
- Java try-with-resources 多个资源关闭顺序是什么
- 2026-09-10 501浏览
-
- 矩阵主副对角线快速定位技巧
- 2026-05-31 501浏览
-
- Java多态优化流程代码与行为分发改进
- 2026-05-26 501浏览
-
- JVM 类元数据双亲委派链表深度解析
- 2026-05-21 501浏览

