如何获取指定JDK类的jmod文件并遍历结构?兼容JDK8及以上
兼容JDK8及更高版本的JDK类库遍历方案
在JDK8及更早版本中,我们可以通过获取类的CodeSource定位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(); // 执行业务逻辑 } }
但从JDK9开始,JDK采用模块化架构(原rt.jar被拆分为多个jmod文件),上述代码会抛出空指针异常:
java.lang.NullPointerException: Cannot invoke "java.security.CodeSource.getLocation()" because the return value of "java.security.ProtectionDomain.getCodeSource()" is null
兼容JDK8与JDK9+的解决方案
以下方案会先尝试JDK8及之前的CodeSource方式,若失败则自动切换为JDK9+的模块化API实现,无需单独分支判断:
import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public void traverseJdkClasses(Class<?> clazz) throws IOException { URL location = null; Module clazzModule = clazz.getModule(); // 优先尝试JDK8及更早版本的方式 try { location = clazz.getProtectionDomain().getCodeSource().getLocation(); } catch (NullPointerException | SecurityException e) { // 模块化环境下,通过模块API获取资源位置 if (clazzModule.isNamed()) { String classResourcePath = "/" + clazz.getName().replace('.', '/') + ".class"; location = clazzModule.getResource(classResourcePath); } } if (location == null) { throw new IOException("无法获取类所在的JDK库位置"); } String locationStr = location.toString(); // 处理JDK9+的jrt:/协议或jmod文件 if (locationStr.startsWith("jrt:/")) { String moduleName = clazzModule.getName(); // 直接访问JRT文件系统遍历模块内容 try (var fs = FileSystems.getFileSystem(URI.create("jrt:/"))) { Path moduleRoot = fs.getPath("/modules/" + moduleName); Files.walk(moduleRoot) .filter(path -> path.toString().endsWith(".class")) .forEach(this::processClassEntry); } } else { // 处理JDK8及之前的jar文件(如rt.jar) File file = new File(location.toURI()); try (JarFile jarFile = new JarFile(file)) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (!entry.isDirectory() && entry.getName().endsWith(".class")) { processClassEntry(entry); } } } } } // 示例业务逻辑方法,可根据需求修改 private void processClassEntry(Object entry) { if (entry instanceof JarEntry) { System.out.println("Jar类条目:" + ((JarEntry) entry).getName()); } else if (entry instanceof Path) { System.out.println("模块类路径:" + ((Path) entry).toString()); } }
关键说明
- 自动适配:通过捕获
NullPointerException和SecurityException,自动切换到模块化逻辑,无需显式判断JDK版本 - JRT文件系统:JDK9+提供
jrt:/协议直接访问模块内容,无需手动定位jmod文件,可靠性更高 - 权限兼容:加入
SecurityException捕获,避免因安全策略限制导致的执行失败
内容的提问来源于stack exchange,提问作者Nicolas Baumann
相关产品推荐
相关产品推荐

