如何在Java 9 +中安全地访问类路径中所有资源文件的URL?
我们从 Java 9 的发行说明中了解到:
应用程序类装入器不再是 java.net.URLClassLoader 的实例(在以前的发行版中从未指定过实现细节)。假定 ClassLoader::getSytemClassLoader 返回 URLClassLoader 对象的代码将需要更新。
这会中断旧代码,该代码将按如下方式扫描类路径:
爪哇 <= 8
URL[] ressources = ((URLClassLoader) classLoader).getURLs();
它遇到一个
java.lang.ClassCastException:
java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to
java.base/java.net.URLClassLoader
因此,对于Java 9 +,以下解决方法被提议作为Apache Ignite项目的PR,它按照预期工作,给定JVM运行时选项中的调整:。但是,正如下面的评论中提到的,这个PR从未合并到他们的主分支中。--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
/*
* Java 9 + Bridge to obtain URLs from classpath...
*/
private static URL[] getURLs(ClassLoader classLoader) {
URL[] urls = new URL[0];
try {
//see https://github.com/apache/ignite/pull/2970
Class builtinClazzLoader = Class.forName("jdk.internal.loader.BuiltinClassLoader");
if (builtinClazzLoader != null) {
Field ucpField = builtinClazzLoader.getDeclaredField("ucp");
ucpField.setAccessible(true);
Object ucpObject = ucpField.get(classLoader);
Class clazz = Class.forName("jdk.internal.loader.URLClassPath");
if (clazz != null && ucpObject != null) {
Method getURLs = clazz.getMethod("getURLs");
if (getURLs != null) {
urls = (URL[]) getURLs.invoke(ucpObject);
}
}
}
} catch (NoSuchMethodException | InvocationTargetException | NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
logger.error("Could not obtain classpath URLs in Java 9+ - Exception was:");
logger.error(e.getLocalizedMessage(), e);
}
return urls;
}
但是,由于在此处使用反射,这会导致一些严重的头痛。这是一种反模式,受到forbidden-apis maven插件的严格批评:
禁止的方法调用:java.lang.reflect.AccessibleObject#setAccessible(布尔值) [反射用法在 SecurityManagers 中无法解决访问标志,并且可能不再适用于 Java 9 中的运行时类]
问题
有没有一种安全的方法可以在OpenJDK 9/10中访问类/模块路径中所有资源的列表,该列表可以由给定的类加载器访问,而无需使用导入(例如,通过使用)?URLs
sun.misc.*
Unsafe
更新(与评论相关)
我知道,我可以做到
String[] pathElements = System.getProperty("java.class.path").split(System.getProperty("path.separator"));
以获取类路径中的元素,然后将它们解析为 s。但是 - 据我所知 - 此属性仅返回应用程序启动时给定的类路径。但是,在容器环境中,这将是应用程序服务器之一,可能还不够,例如,使用 EAR 捆绑包。URL
更新 2
感谢您的所有评论。我将测试,如果能达到我们的目的,并更新问题,如果这满足了我们的需求。System.getProperty("java.class.path")
然而,似乎其他项目(也许是出于其他原因,例如Apache TomEE 8)遭受了与之相关的相同痛苦 - 出于这个原因,我认为这是一个有价值的问题。URLClassLoader
更新 3
最后,我们确实切换到了 classgraph 并将我们的代码迁移到此库,以解决我们的用例,以便从类路径中加载捆绑为 JAR 的 ML 资源。