本文介紹了如何在Java 9+中安全地訪問類路徑中所有資源文件的URL?的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧!
問題描述
我們從Java 9的發(fā)行說明中了解到
應(yīng)用程序類加載器不再是java.net.URLClassLoader的實例(這是以前版本中從未指定的實現(xiàn)細(xì)節(jié))。假定ClassLoader::getSytemClassLoader返回URLClassLoader對象的代碼將需要更新。
這打破了舊代碼,舊代碼按如下方式掃描類路徑:
Java<;=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+,提出了以下解決方法作為PR at the Apache Ignite Project,在給定JVM運行時選項中的調(diào)整后,該方法可以按預(yù)期工作:--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
。但是,正如下面的評論中所提到的,此PR從未合并到他們的主分支。
/*
* 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;
}
但是,由于在這里使用Reflection,這會導(dǎo)致一些嚴(yán)重的頭痛。這是一種反模式,受到了forbidden-apis maven plugin:
的嚴(yán)厲批評
禁止的方法調(diào)用:java.lang.reflect.AccessibleObject#setAccessible(boolean)[使用反射來解決訪問標(biāo)志在SecurityManager中失敗,并且可能不再適用于JAVA 9中的運行時類]
問題
在OpenJDK 9/10中,是否有安全訪問類/模塊路徑中所有資源的URLs
列表的方法,而無需使用sun.misc.*
導(dǎo)入(例如,使用Unsafe
)?
更新(與評論相關(guān))
我知道,我能做到
String[] pathElements = System.getProperty("java.class.path").split(System.getProperty("path.separator"));
獲取類路徑中的元素,然后將它們解析為URL
。然而,據(jù)我所知,此屬性僅返回應(yīng)用程序啟動時給定的類路徑。然而,在容器環(huán)境中,這將是應(yīng)用程序服務(wù)器的一個,并且可能是不夠的,例如,然后使用EAR包。
更新2
感謝您的所有評論。我將測試System.getProperty("java.class.path")
是否可用于我們的目的,如果這能滿足我們的需要,我將更新問題。
然而,似乎其他項目(可能是因為其他原因,例如ApacheTomee 8)遭受了與URLClassLoader
相同的痛苦-因此,我認(rèn)為這是一個有價值的問題。
更新3
最后,我們確實切換到classgraph,并將代碼遷移到該庫以解決我們的用例,以便從類路徑加載捆綁為JAR的ML資源。
推薦答案
我認(rèn)為這是an XY problem。訪問類路徑上所有資源的URL在Java中是不受支持的操作,嘗試這樣做也不是一件好事。正如您在這個問題中已經(jīng)看到的,如果您試圖這樣做,您將一直與框架作斗爭。將會有一百萬個邊緣案例破壞您的解決方案(定制類加載器、EE容器等)。
請詳細(xì)說明您為什么要這樣做?
如果您有某種插件系統(tǒng),并且正在尋找可能在運行時提供的與您的代碼交互的模塊,那么您應(yīng)該使用the ServiceLoader API,即:
通過在資源目錄
META-INF/services
中放置一個提供者配置文件來標(biāo)識打包為類路徑的JAR文件的服務(wù)提供者。提供程序配置文件的名稱是服務(wù)的完全限定二進(jìn)制名稱。提供者配置文件包含服務(wù)提供者的完全限定二進(jìn)制名稱的列表,每行一個。
例如,假設(shè)服務(wù)提供者com.example.impl.StandardCodecs
打包在類路徑的JAR文件中。JAR文件將包含一個名為的提供程序配置文件
META-INF/services/com.example.CodecFactory
包含行:
com.example.impl.StandardCodecs # Standard codecs
這篇關(guān)于如何在Java 9+中安全地訪問類路徑中所有資源文件的URL?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,