1. 程式人生 > 其它 >ClassLoader載入專案下的資原始檔

ClassLoader載入專案下的資原始檔

技術標籤:java人生java實戰案例

Bootstrap、ExtClassLoader、AppClassLoader

首先先要知道三種類載入器的作用。
啟動類載入器(Bootstrap
啟動類載入器是C++實現的,負責將<JAVA_HOME>/lib路徑下的核心類庫或-Xbootclasspath引數指定的路徑下的jar包載入到記憶體中,出於安全考慮,Bootstrap只加載包名為java、javax、sun等開頭的類

##擴充套件類載入器(ExtClassLoader
ExtClassLoaderJava實現的,是sun.misc.Launcher的靜態內部類,負責載入<JAVA_HOME>/lib/ext

目錄下或者由系統變數-Djava.ext.dir指定路徑中的類庫

##系統類載入器(AppClassLoader
AppClassLoader也是Java實現的,同樣也是sun.misc.Launcher的靜態內部類,負責載入系統類路徑java -classpath-D java.class.path指定路徑下的類庫。我們口中常說的classpath就是AppClassLoader負責載入的。通過ClassLoader#getSystemClassLoader()方法可以獲取到該類載入器

ClassLoader載入資源

我們啟動服務時,IDE會輸出當前的Java環境和-classpath-classpath

指定了所有JVM要載入的檔案和路徑,裡面一定包含了你當前專案編譯後的路徑target/classes路徑。
在這裡插入圖片描述
單元測試的-classpath先指定了target/test-classes,然後是target/classes
在這裡插入圖片描述
這就意味著,使用ClassLoader在單元測試獲取資源和正常服務獲取資源有差別。

下面是用web服務做的測試

classpath

-classpath只有target/classes路徑,載入資源配置檔案時,如果不寫資源名,只寫./、""、/

  • src下的main函式中使用ClassLoaderClassPathResource獲取資原始檔
    使用ClassLoader#getResource(String name)

    • name=./返回target/classpath/
    • name=""返回在target/classpath/
    • name=/返回null

    使用new ClassPathResource(String path)
    path=/、./、"",都表示target/classes/,對於path=/Spring做了處理

  • 啟動的服務,使用ClassLoaderClassPathResource獲取資原始檔
    對於/、./、""ClassLoaderClassPathResource都表示target/classes/

test-classpath

-classpath先是target/test-classes,然後是target/classes,載入資源配置檔案時,如果不寫資源名,只寫./、""、/

  • 單元測試的main函式中使用ClassLoaderClassPathResource獲取資原始檔
  • 單元測試方法中呼叫服務,服務中使用ClassLoaderClassPathResource獲取資原始檔
  • 單元測試方法中使用ClassLoaderClassPathResource獲取資原始檔

以上三種,都是優先從target/test-classes載入相應的資原始檔,如果沒有,再去target/classes下載入資源。
使用ClassLoader時,./和""返回target/test-classes/返回null
使用ClassPathResource時,/、./和""都返回target/test-classes

ClassLoader#getResource(String name)和Class#getResource(String name)的異同

  • ClassLoader#getResource(String name)
    ClassLoader使用雙親委派模型去查詢檔案資源,先Bootstrap,再ExtClassLoader,最後AppClassLoader,返回的是target/classes或者target/test-classes
public URL getResource(String name) {
    URL url;
    if (parent != null) {
        url = parent.getResource(name);
    } else {
        url = getBootstrapResource(name);
    }
    if (url == null) {
        url = findResource(name);
    }
    return url;
}
  • Class#getResource(String name)
    原始碼中先將檔案路徑定位到當前檔案所在的路徑下,然後優先獲取使用者使用的Class中的ClassLoader,如果為null(一般情況下都是AppClassLoader),呼叫ClassLoader.getSystemResource(name);方法,這個方法中呼叫了ClassLoader#getSystemClassLoader(),此方法通常返回的是AppClassLoader
public java.net.URL getResource(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResource(name);
    }
    return cl.getResource(name);
}

private String resolveName(String name) {
    if (name == null) {
        return name;
    }
    if (!name.startsWith("/")) {
        Class<?> c = this;
        while (c.isArray()) {
            c = c.getComponentType();
        }
        String baseName = c.getName();
        int index = baseName.lastIndexOf('.');
        if (index != -1) {
            name = baseName.substring(0, index).replace('.', '/')
                +"/"+name;
        }
    } else {
        name = name.substring(1);
    }
    return name;
}