【问题标题】:Java empty path convention, especially that used in ClassLoader.getResourcesJava 空路径约定,尤其是 ClassLoader.getResources 中使用的约定
【发布时间】:2017-08-10 23:36:27
【问题描述】:

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

大概是因为 Java System ClassLoader 是加载我自己的应用程序类的三个 ClassLoader 之一(c.f.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

【问题讨论】:

  • 该链接有助于清除 ClassLoader 以及 Custom ClassLoader,在链接末尾我们可以看到,当 JVM 使用 Java 选项启动时,我们如何将自定义类加载器设为默认值. journaldev.com/349/java-classloader

标签: java classloader urlclassloader


【解决方案1】:

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

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

...以及如何?

默认的 OpenJDK 应用程序 ClassLoader(也称为 System ClassLoadersun.misc.Launcher$AppClassLoaderURLClassLoader 的后代,其 URL 搜索路径包含“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,根据URLClassLoader 对术语“资源”的具体定义,指定在其搜索路径返回文件、目录或JAR 条目。当搜索路径条目表示目录时,它也恰好返回搜索路径条目本身这一事实并未在其规范中得到解决,因此应将其视为实现细节(也可能是轻微的规范违规),而不是依赖于.同样,它不返回 JAR 搜索路径条目这一事实虽然与前者不一致,但并不违反其规范。

另一方面,

getURLs 返回实例化时提供的确切1 搜索路径条目。

实施方面...
sun.misc.URLClassPath$FileLoader 不同,如前所述,它根据每个搜索路径条目的文件系统路径解析资源名称,sun.misc.URLClassPath$JarLoader 尝试通过JarFile.getEntry(name) 直接匹配,对于“”,最有可能是“.”,条目名字,显然失败了。但是即使URLClassPath.Loaders 都以相同的方式解释资源名称,事情也不会按预期进行,因为嵌入式 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的来源。

【讨论】:

  • Uux,您提到 JAR 类路径条目不会包含在 getResources 中。我对此进行了测试,您是绝对正确的。但是,如果我使用((URLClassLoader) classLoader).getURLs();,为什么它们会被包含在内(请参阅上面有问题的代码添加)? classloader 的类型没有改变...
  • 如果我可以再澄清一件事:我们怎么知道ClassLoader.getSystemClassLoader();AppClassLoader 类型?查看getSystemClassLoader() 的源代码并不是很明显,因为它的返回类型只是Classloader
  • 我已经编辑了我的答案 - 如果需要进一步说明,请告诉我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-23
  • 1970-01-01
  • 1970-01-01
  • 2013-12-12
  • 2012-03-26
相关资源
最近更新 更多