当前位置:首页 > 文章列表 > 文章 > java教程 > Java ModuleDescriptor.Provides 怎样读取服务提供者的类名

Java ModuleDescriptor.Provides 怎样读取服务提供者的类名

来源:17golang原创 2026-11-06 19:57:15 0浏览 收藏

先从 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() 中。示例按服务名称排序,目的是让报告排列稳定,而不是设置服务选择优先级。

模块描述符、Provides声明与服务和提供者名称字段关系
服务声明结构说明图:模块声明分组关联 Provides,名称字段分组区分 service() 和 providers()。图中连线表示静态结构,不是服务创建流程或运行截图。

给名称列表保留模块与服务上下文

生产报告中只保存一个类名不够:它属于哪个模块,为哪个服务类型声明,也应一起记录。可把一条报告行理解为模块名、服务类型名和提供者名称列表三个字段;不要把多个模块同名服务的声明混成一条没有来源的字符串。

例如上面的 demo.impl.ReportFactory 只是放入描述符的名称。API 文档允许该列表表示提供者或提供者工厂,因此报告字段宜叫“声明的提供者或工厂”,不能无条件标成“接口实现类”。

providers() 的返回列表非空且不可修改。如果需要排序展示,先创建副本再排序,保留原声明列表,避免对不可修改列表调用 sort() 导致异常。

// entry 是上文循环中的一项服务声明,只改展示副本。
List displayedNames = 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 返回不可修改列表。需要过滤或排序时新建副本,不把元数据对象当成可变配置。

读取到的名称一定是实现接口的类吗?

不能这样假设。文档明确包含提供者工厂名称;元数据读取本身也不验证实际创建能否成功。

这个方法会实例化服务吗?

不会。本文调用描述符的名称访问方法,只读取声明字段;实际服务发现与创建不在该片段内。

参考官方文档:Provides 名称访问方法、ModuleDescriptor、运行时 Module。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Java 25 flexible constructor bodies 怎样在 super 前准备参数Java 25 flexible constructor bodies 怎样在 super 前准备参数
上一篇
Java 25 flexible constructor bodies 怎样在 super 前准备参数
Java StructuredTaskScope 怎样收集部分成功结果
下一篇
Java StructuredTaskScope 怎样收集部分成功结果
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    912次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    919次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1003次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    995次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    815次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码