有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java空路径约定,特别是在类加载器中使用的。获取资源

今天,我惊讶地(可能是因为缺乏经验)发现,您可以,而且实际上,将一个空路径(字面上是一个空字符串)传递给ClassLoader.getResources,即。 ClassLoader.getSystemClassLoader().getResources("")。通过一些测试,这将返回我的应用程序.class文件所在的一个或两个目录(不包括第三方软件包的目录)。(示例用法:Get all of the Classes in the Classpath。)

这大概是因为Java系统类加载器是加载我自己的应用程序类(c.f.http://www.oracle.com/technetwork/articles/javase/classloaders-140370.html)的三个类加载器之一,所以URL返回指向我的应用程序类文件目录也就不足为奇了

但为什么,以及如何,空字符串实现了这一点?我没有发现文件。这个空路径是更常见的Java约定的派生吗?它当然不是Linux——你不能在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


共 (1) 个答案

  1. # 1 楼答案

    当资源名为“”或“”时,getResources(String)调用为什么会匹配作为目录的所有类路径项

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

    。。。怎么做

    默认的OpenJDK应用程序ClassLoader(也称为系统ClassLoader),sun.misc.Launcher$AppClassLoader是一个URLClassLoader子体,其URL搜索路径包含“java.class.path”系统属性的值。它的getResourcesgetResource)方法最终委托给^{},该方法执行以下操作:

    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参数为“or”匹配搜索路径条目本身。换句话说,所有顶级类路径条目都被匹配并返回,就好像它们都位于同一根目录下一样。注意,这不适用于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的源代码