如何从VirtualMachine中获取类目录形式的类路径列表?
提取RawCommandLineLauncher启动的VirtualMachine类路径及类目录方案
完全可以实现,以下是几种基于JDI公开API的可靠方法,无需依赖非公开接口:
1. 直接读取目标VM的系统类路径
目标VM启动时指定的类路径会存在系统属性java.class.path中,通过VirtualMachine的getSystemProperties()就能直接获取:
VirtualMachine vm = ...; // 你的VM实例 String classPathStr = vm.getSystemProperties().getProperty("java.class.path"); String[] classPathEntries = classPathStr.split(File.pathSeparator); // classPathEntries就是所有类路径项(目录、jar包)
这是最直接的方式,能拿到启动时配置的完整类路径。
2. 通过ClassLoader镜像获取动态加载的资源路径
如果需要获取ClassLoader实际加载类的路径,可通过JDI调用目标VM中ClassLoader的方法:
// 获取系统ClassLoader的镜像类型 ClassType classLoaderType = (ClassType) vm.classesByName("java.lang.ClassLoader").get(0); // 获取系统ClassLoader实例镜像 ObjectReference systemCL = classLoaderType.invokeMethod( vm.eventRequestManager().createDefaultThread(), classLoaderType.methodsByName("getSystemClassLoader").get(0), Collections.emptyList(), 0 ); // 调用getResource获取某类的URL Method getResource = classLoaderType.methodsByName("getResource").get(0); ObjectReference urlMirror = (ObjectReference) classLoaderType.invokeMethod( vm.eventRequestManager().createDefaultThread(), getResource, List.of(vm.mirrorOfString("java/lang/Object.class")), 0 ); // 提取URL的路径 if (urlMirror != null) { ClassType urlType = (ClassType) vm.classesByName("java.net.URL").get(0); Method getPath = urlType.methodsByName("getPath").get(0); String path = (String) urlType.invokeMethod( vm.eventRequestManager().createDefaultThread(), getPath, Collections.emptyList(), 0 ); // 处理路径:若来自jar则截去内部路径 if (path.contains("!")) { path = path.substring(0, path.indexOf("!")); } }
这种方式适合获取动态加载的类资源路径,而非仅启动时配置的类路径。
3. 结合allClasses()提取已加载类的来源路径
你提到的allClasses()方法也能用来提取类目录,对每个ReferenceType调用classFileURL()即可获取类文件的来源URL:
Set<String> classDirs = new HashSet<>(); for (ReferenceType rt : vm.allClasses()) { URL classUrl = rt.classFileURL(); if (classUrl != null) { String path = classUrl.getPath(); // 处理jar包内的类路径,提取jar包或目录路径 if (path.endsWith(".jar!/" + rt.name().replace('.', '/') + ".class")) { path = path.substring(0, path.indexOf("!")); } else if (path.endsWith(rt.name().replace('.', '/') + ".class")) { path = path.substring(0, path.length() - rt.name().length() - 6); // 去掉类文件名 } classDirs.add(path); } } // classDirs就是去重后的类目录/jar包路径集合
关于你之前尝试的问题
- 获取ClassLoader失败:你可能是试图在本地VM中直接获取目标VM的ClassLoader对象,这是错误的——JDI中所有目标VM的对象都是镜像(Mirror),必须通过JDI的方法调用目标VM内的代码来操作。
- 依赖JDI非公开方法:完全没必要,上述所有方法都基于JDI公开API,兼容性更好。
内容的提问来源于stack exchange,提问作者grazing cow
相关产品推荐
相关产品推荐

