簡體   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