如何获取给定JDK类的jmod文件和jmod的遍历树?

How do I get the jmod file of a given JDK class and walk tree of jmod?

提问人:Sybuser 提问时间:11/10/2022 最后编辑:HolgerSybuser 更新时间:11/10/2022 访问量:267

问:

直到 JDK8,我可以使用以下内容来迭代 rt.jar 类。给定一个类,我可以找到所有其他类:

final URL location = clazz.getProtectionDomain().getCodeSource().getLocation();
final File file = new File(location.toURI());
try (JarFile jarFile = new JarFile(file)) {
    final Enumeration<JarEntry> entries = jarFile.entries();
    while (entries.hasMoreElements()) {
        final JarEntry jarEntry = entries.nextElement();
        // do something...
    }
}

在 JDK8 之后,使用它不再有效:clazz.getProtectionDomain().getCodeSource().getLocation()

java.lang.NullPointerException: Cannot invoke "java.security.CodeSource.getLocation()" because the return value of "java.security.ProtectionDomain.getCodeSource()" is null

有没有好的替代品? 我正在考虑做一个这样的特殊情况:

if (clazz.getProtectionDomain().getCodeSource() == null) {
  // find URL to the jmod ...
}

但是,在这两种情况下都有效的解决方案会更可取。

java-module java-platform-module-system

评论


答:

2赞 Holger 11/10/2022 #1

您的问题中有一个错误的假设,因为类永远不会从 .jmod 文件加载

您可以获取模块的位置,例如

Module m = clazz.getModule();
System.out.println(m.getLayer().configuration()
    .findModule(m.getName()).flatMap(rm -> rm.reference().location())
    .orElse(null));

这不适用于“未命名模块”,读取通过类路径加载的类,因此您必须求助于 if is ..getProtectionDomain().getCodeSource().getLocation()m.isNamed()false

但是,对于内置模块,URI 将始终为 ,因此要遍历平台类,您根本不需要它。jrt:/module-name

例如,以下代码片段列出了包中的所有类:java.lang

try(var list = Files.list(Paths.get(URI.create("jrt:/java.base/java/lang")))) {
         list.map(p -> p.getFileName().toString())
             .filter(s -> s.endsWith(".class"))
             .map(s -> "java.lang." + s.substring(0, s.length() - 6))
             .forEach(System.out::println);
}

而要获取 的类文件字节,您可以简单地使用java.lang.Object

byte[] objectClassFile = Files.readAllBytes(
    Paths.get(URI.create("jrt:/java.base/java/lang/Object.class")));