Java空路径约定,特别是在ClassLoader.getResources中使用的

Java空路径约定,特别是在ClassLoader.getResources中使用的,java,classloader,urlclassloader,Java,Classloader,Urlclassloader,今天,我惊讶地发现(可能是由于缺乏经验),您可以将一个空路径(字面上是一个空字符串)传递给ClassLoader.getResources,也就是。 ClassLoader.getSystemClassLoader().getResources(“”)。通过一些测试,这将返回我的应用程序.class文件所在的一个或两个目录(不包括第三方软件包的目录)。(示例用法:) 这大概是因为Java系统类加载器是加载我自己的应用程序类(c.f.)的三个类加载器之一,所以URL返回指向我的应用程序类文件目录也

今天,我惊讶地发现(可能是由于缺乏经验),您可以将一个空路径(字面上是一个空字符串)传递给
ClassLoader.getResources
,也就是。
ClassLoader.getSystemClassLoader().getResources(“”)
。通过一些测试,这将返回我的应用程序
.class
文件所在的一个或两个目录(不包括第三方软件包的目录)。(示例用法:)

这大概是因为Java系统类加载器是加载我自己的应用程序类(c.f.)的三个类加载器之一,所以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
        }
    }
}
公共类myTest{
公共静态void main(字符串[]args)引发异常{
ClassLoader ClassLoader=ClassLoader.getSystemClassLoader();
URL[]URL=((URLClassLoader)classLoader.getURL();
for(int n=0;n

要执行的命令:
java-cp.“:external.jar”myTest

为什么
getResources(String)
调用在资源名为“”或“”时匹配所有目录类路径项?

我只能猜测。对于它的价值,我认为这是具体的<代码> ClassLoader < /代码>的实现细节。然而,从文件系统用户的角度来看,“”和“.”资源名称的处理方式有些直观

…怎么做?

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

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搜索路径条目的事实,虽然与前者不一致,但并不违背其规范

另一方面,
getURL
,返回实例化时提供的exact1搜索路径条目

实现方面…
与前面看到的根据每个搜索路径条目的文件系统路径解析资源名的
sun.misc.URLClassPath$FileLoader
不同,
sun.misc.URLClassPath$JarLoader
尝试通过
JarFile.getEntry(name)
直接匹配,对于“”和最可能的“”,条目名显然失败。但是,即使两个
URLClassPath.Loader
s以相同的方式解释资源名称,事情也不会像预期的那样进行,因为嵌入式JAR文件系统不支持根目录的概念

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

要独立于系统
类加载器执行此操作,请使用

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这可能并不总是正确的,例如,当搜索路径条目“重叠”(一个是另一个的父项)或安全约束出现时