今天,我很惊讶(可能是由于缺乏经验)发现你可以,并且实际上有用的是,将一条空路径(字面上是一个空字符串)传递给ClassLoader.getResources
,即
ClassLoader.getSystemClassLoader().getResources("")
。从某些测试中,这将返回我的应用程序.class
文件所在的一个或两个目录(并且不包括第三方软件包的目录)。 (示例用法:Get all of the Classes in the Classpath。)
据推测,这是因为Java System ClassLoader是加载我自己的应用程序类的三个ClassLoader之一(参见http://www.oracle.com/technetwork/articles/javase/classloaders-140370.html),所以URL返回指向我的目录并不奇怪应用程序类文件。
但是为什么以及如何使用空字符串实现这一目标呢?我没有发现它有记录。这是更常见的Java约定的空路径衍生物吗?它肯定不是Linux - 你可以在bash中cd
进入一条空路。如果有人能帮助我理解这一点,我会很感激。
另一方面,我注意到getResources(".")
实现了同样的目标。
评论讨论的补充
public class myTest {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) classLoader).getURLs();
for (int n = 0; n < urls.length; n++)
System.out.println(urls[n]); //lists external.jar
Enumeration<URL> roots = classLoader.getResources(".");
while (roots.hasMoreElements()) {
URL url = roots.nextElement();
System.out.println("getResources: " + url); //does not list external.jar
}
}
}
执行命令:java -cp ".:external.jar" myTest
答案 0 :(得分:1)
当给定资源名称为“”或“。”时,为什么getResources(String)
调用会匹配作为目录的所有类路径条目?
我只能推测。对于它的价值,我认为这是特定ClassLoader
的实现细节。方式“”和“。”从文件系统用户的角度来看,资源名称的处理方式有些直观。
......以及如何?
默认的OpenJDK应用ClassLoader
(也称为系统 ClassLoader
),sun.misc.Launcher$AppClassLoader
是URLClassLoader
后代,带有网址搜索包含“java.class.path”系统属性值的路径。其getResources
(getResource
)方法最终委托给sun.misc.URLClassPath$FileLoader.getResource(String, boolean)
,其执行以下操作:
url = new URL(getBaseURL(), ParseUtil.encodePath(name, false));
...
file = new File(dir, name.replace('/', File.separatorChar)); // dir is the equivalent of getBaseURL()'s path component
...
if (file.exists()) {
return new sun.misc.Resource() {
...
public URL getURL() { return url; } // eventually returned by the ClassLoader
}
}
将所有URL解析放在一边,资源名称基本上被视为相对文件系统路径,并且对于加载器的搜索路径条目是“绝对化的”。因此name
参数为“”或“。”匹配搜索路径条目本身。换句话说,所有顶级类路径条目都匹配并返回,就好像它们都位于同一根目录下一样。请注意,这不适用于JAR类路径条目,而是由sun.misc.URLClassPath$JarLoader
处理。
为什么这些getResources
调用也不匹配JAR类路径条目?为什么这些类路径条目包含在由URLClassLoader.getURLs()
返回的数组中?
API-明智...
这是两种不相关的方法,每种方法都有不同的用途。有时他们“只是发生”产生相同或相似的输出 - 然而他们的规格却暗示任何形式的行为相互一致。
getResources
被指定为在其搜索路径下返回文件,目录或JAR条目。当它们表示目录时,它也恰好返回搜索路径条目本身的事实,它的规范没有解决它,因此应该被视为实现细节(也可能是次要的规范违规),而不是依赖它们。同样,它不返回JAR搜索路径条目,但与前者不一致,这一事实并不反对其规范。
URLClassLoader
返回在实例化时提供的确切 1 搜索路径条目。
实施明智...
与getURLs
不同,正如前面所述,它针对每个搜索路径条目的文件系统路径解析资源名称,sun.misc.URLClassPath$FileLoader
尝试通过sun.misc.URLClassPath$JarLoader
进行直接匹配,对于“”和“{1}}可能“。”,条目名称,显然失败了。但即使两个JarFile.getEntry(name)
都以相同的方式解释资源名称,事情也无法按预期工作,因为嵌入式JAR文件系统不支持根目录的概念。
那么我应该如何检索所有类路径条目?
要独立于有效的系统URLClassPath.Loader
执行此操作,请使用
ClassLoader
,最好在String[] classPathEntries = System.getProperty("java.class.path").split(File.pathSeparator);
方法的早期,在任何第三方代码提供修改属性的机会之前。
main
的返回类型为ClassLoader.getSystemClassLoader()
。我们如何知道(肯定)返回的实例是java.lang.ClassLoader
?
我们真的没有。系统类加载器是依赖于实现和可替换的。我们所能做的一切都是测试,例如,
sun.misc.Launcher$AppClassLoader
,并采取相应行动。
<小时/> 1 这可能并不总是成立,例如,当搜索路径条目“重叠”(一个是另一个父的),或者安全约束适用时;有关详细信息,请参阅try {
ClassLoader sysCl = ClassLoader.getSystemClassLoader();
// not using single-arg Class.forName, since it would use the ClassLoader of this class,
// which, in the worst-case scenario of being a non-delegating loader, could attempt to load AppClassLoader itself
if (Class.forName("sun.misc.Launcher$AppClassLoader", false, sysCl).isAssignableFrom(sysCl.getClass())) {
// default implementation, _most likely_ a URLClassLoader subclass
}
else {
// System ClassLoader overridden, or not on OpenJDK
}
}
catch (ReflectiveOperationException roe) {
// most likely not on OpenJDK
}
的来源。