繁体   English   中英

Java空路径约定,尤指在ClassLoader.getResources中使用的约定

[英]Java empty path convention, especially that used in ClassLoader.getResources

今天,我很惊讶(可能是由于缺乏经验)发现你可以,并且它实际上很有用,将一个空路径(字面上是一个空字符串)传递给ClassLoader.getResources ,即ClassLoader.getSystemClassLoader().getResources("") 从某些测试中,这将返回我的应用程序.class文件所在的一个或两个目录(并且不包括第三方软件包的目录)。 (示例用法: 获取Classpath中的所有类 。)

据推测,这是因为Java System ClassLoader是加载我自己的应用程序类的三个ClassLoader之一(参见http://www.oracle.com/technetwork/articles/javase/classloaders-140370.html ),所以这并不奇怪该URL返回指向我的应用程序类文件的目录。

但是为什么,以及如何,空字符串实现这一目标? 我没有发现它有记录。 这是更常见的Java约定的空路径衍生物吗? 这当然不是Linux的-你不能cd到在bash空路径。 如果有人能帮助我理解这一点,我会很感激。

在另一个注释中,我注意到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

当给定资源名称“”或“。”时,为什么getResources(String)调用匹配作为目录的所有类路径条目?

我只能推测。 对于它的价值,我认为这是特定ClassLoader的实现细节。 方式“”和“。” 从文件系统用户的角度来看,资源名称的处理方式有点直观。

...如何?

默认的OpenJDK应用程序ClassLoader (也称为System ClassLoadersun.misc.Launcher$AppClassLoader是一个URLClassLoader后代,其URL搜索路径包含“java.class.path”系统属性的值。 它的getResourcesgetResource )方法最终委托给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明智...
这是两种不相关的方法,每种方法都有不同的用途。 有时他们“只是发生”以产生相同或相似的输出 - 然而他们的规格却暗示任何形式的行为相互一致。

根据URLClassLoader对术语“资源”的具体定义, getResources被指定为其搜索路径返回文件,目录或JAR条目。 当它们表示目录时,它也恰好返回搜索路径条目本身的事实,它的规范没有解决它,因此应该被视为实现细节(也可能是次要的规范违规),而不是依赖它们。 同样,它不返回JAR搜索路径条目,而与前者不一致,这一事实并不反对其规范。

另一方面, getURLs返回在实例化时提供的确切的1个搜索路径条目。

实现明智...
sun.misc.URLClassPath$FileLoader不同,如前所述,它根据每个搜索路径条目的文件系统路径解析资源名称, sun.misc.URLClassPath$JarLoader尝试通过JarFile.getEntry(name)进行直接匹配,对于“”,很可能是“。”,条目名称,显然都失败了。 但即使两个URLClassPath.Loader都以相同的方式解释资源名称,事情也无法按预期工作,因为嵌入式JAR文件系统不支持根目录的概念。

那么我应该如何检索所有类路径条目?

要独立于系统ClassLoader生效,请使用类似的东西

String[] classPathEntries = System.getProperty("java.class.path").split(File.pathSeparator);

,最好在您的main方法的早期,在任何第三方代码提供修改属性的机会之前。

ClassLoader.getSystemClassLoader()的返回类型为java.lang.ClassLoader 我们怎么知道(肯定)返回的实例是sun.misc.Launcher$AppClassLoader

我们真的没有。 系统类加载器是依赖于实现和可替换的。 我们所能做的一如既往地是测试,例如,

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
}

并采取相应行动。


1 这可能并不总是成立,例如,当搜索路径条目“重叠”(一个是另一个父母)或安全约束适用时; 有关详细信息,请参阅sun.misc.URLClassPath的源代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM