ClassLoader載入專案下的資原始檔
Bootstrap、ExtClassLoader、AppClassLoader
首先先要知道三種類載入器的作用。
啟動類載入器(Bootstrap
)
啟動類載入器是C++
實現的,負責將<JAVA_HOME>/lib
路徑下的核心類庫或-Xbootclasspath
引數指定的路徑下的jar
包載入到記憶體中,出於安全考慮,Bootstrap
只加載包名為java、javax、sun
等開頭的類
##擴充套件類載入器(ExtClassLoader
)
ExtClassLoader
是Java
實現的,是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
函式中使用ClassLoader
和ClassPathResource
獲取資原始檔
使用ClassLoader#getResource(String name)
name=./
返回target/classpath/
name=""
返回在target/classpath/
name=/
返回null
使用
new ClassPathResource(String path)
時
path=/、./、""
,都表示target/classes/
,對於path=/
,Spring
做了處理 -
啟動的服務,使用
ClassLoader
和ClassPathResource
獲取資原始檔
對於/、./、""
,ClassLoader
和ClassPathResource
都表示target/classes/
test-classpath
-classpath
先是target/test-classes
,然後是target/classes
,載入資源配置檔案時,如果不寫資源名,只寫./、""、/
時
- 單元測試的
main
函式中使用ClassLoader
和ClassPathResource
獲取資原始檔 - 單元測試方法中呼叫服務,服務中使用
ClassLoader
和ClassPathResource
獲取資原始檔 - 單元測試方法中使用
ClassLoader
和ClassPathResource
獲取資原始檔
以上三種,都是優先從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;
}