Class類下的getResource和ClassLoader類下的getResource方法使用和區(qū)別
通過(guò)getResource(String name)
方法寸齐,我們能獲得一個(gè)URL對(duì)象讀取資源
先說(shuō)結(jié)論:
1. MyClass.class.getResource("xxx")方法中傳的參數(shù)如果是相對(duì)路徑饲窿,那么傳遞的路徑是相對(duì)于MyClass而言
2. MyClass.class.getResource("/xxx")方法中傳的參數(shù)如果是絕對(duì)路徑,那么傳遞的路徑是相對(duì)于classpath而言
3.MyClass.class.getClassLoader().getResource("xxx")方法中傳的參數(shù)如果是相對(duì)路徑,那么傳遞的路徑是相對(duì)于classpath而言
4.MyClass.class.getClassLoader().getResource("/xxx")方法中傳的參數(shù)如果是絕對(duì)路徑,也就是只要參數(shù)開(kāi)頭帶/那么返回的都是null
案例說(shuō)明
目錄結(jié)構(gòu)
測(cè)試類
public class Test {
public static void main(String[] args) {
}
private static void testMethod(String name) {
URL classUrl = ClassTwo.class.getResource(name);
URL classLoaderUrl = ClassTwo.class.getClassLoader().getResource(name);
System.out.println("傳遞的參數(shù)是:" + name);
System.out.println("ClassTwo類下的getResource方法獲取的路徑:" + classUrl);
System.out.println("ClassTwo類的ClassLoader下的getResource方法獲取的路徑:" + classLoaderUrl);
System.out.println("=====================================================================================================");
}
}
方法調(diào)用
下面對(duì)傳遞不同路徑時(shí)菜职,Class的getResource(String name)方法和ClassLoader的getResource(String name)方法所返回的結(jié)果進(jìn)行比較廉赔。
1.當(dāng)傳入的參數(shù)是空串時(shí)
public static void main(String[] args) {
// 當(dāng)傳入的參數(shù)是空串時(shí)
String name = "";
testMethod(name);
}
返回的結(jié)果:
傳遞的參數(shù)是:
ClassTwo類下的getResource方法獲取的路徑:file:/H:/Wu/IDEAWorkspce2/gupao/demo/target/classes/com/wsp/
ClassTwo類的ClassLoader下的getResource方法獲取的路徑:file:/H:/Wu/IDEAWorkspce2/gupao/demo/target/classes/
2.當(dāng)傳入的參數(shù)是 / 時(shí)
public static void main(String[] args) {
// 當(dāng)傳入的參數(shù)是 / 時(shí)
String name = "/";
testMethod(name);
}
返回的結(jié)果是:
傳遞的參數(shù)是:/
ClassTwo類下的getResource方法獲取的路徑:file:/H:/Wu/IDEAWorkspce2/gupao/demo/target/classes/
ClassTwo類的ClassLoader下的getResource方法獲取的路徑:null
3.當(dāng)傳入的參數(shù)是ClassTwo類的相對(duì)路徑時(shí)
public static void main(String[] args) {
// 當(dāng)傳入的參數(shù)是ClassTwo類的相對(duì)路徑時(shí)
String name = "test";
testMethod(name);
}
返回的結(jié)果是:
傳遞的參數(shù)是:test
ClassTwo類下的getResource方法獲取的路徑:file:/H:/Wu/IDEAWorkspce2/gupao/demo/target/classes/com/wsp/test
ClassTwo類的ClassLoader下的getResource方法獲取的路徑:null
4.當(dāng)傳入的參數(shù)是classpath的相對(duì)路徑時(shí)
public static void main(String[] args) {
// 當(dāng)傳入的參數(shù)是classpath的相對(duì)路徑時(shí)
String name = "com/wsp";
testMethod(name);
}
返回的結(jié)果:
傳遞的參數(shù)是:com/wsp
ClassTwo類下的getResource方法獲取的路徑:null
ClassTwo類的ClassLoader下的getResource方法獲取的路徑:file:/H:/Wu/IDEAWorkspce2/gupao/demo/target/classes/com/wsp
5.當(dāng)傳入的參數(shù)是classpath的絕對(duì)路徑時(shí)
public static void main(String[] args) {
// 當(dāng)傳入的參數(shù)是classpath的絕對(duì)路徑時(shí)
String name = "/com/wsp";
testMethod(name);
}
返回的結(jié)果:
傳遞的參數(shù)是:/com/wsp
ClassTwo類下的getResource方法獲取的路徑:file:/H:/Wu/IDEAWorkspce2/gupao/demo/target/classes/com/wsp
ClassTwo類的ClassLoader下的getResource方法獲取的路徑:null
使用小結(jié)
通過(guò)上面的例子肉微,簡(jiǎn)單來(lái)說(shuō)就是如果使用ClassLoader的getResource(String name)方法時(shí),傳遞的路徑不能是絕對(duì)路徑蜡塌,且傳遞的路徑必須是相對(duì)于classpath的路徑碉纳,如果傳入""就相當(dāng)于獲取classpath路徑。使用Class的getResource(String name)方法岗照,如果傳遞的是絕對(duì)路徑則是相對(duì)于classpath而已村象,如果傳遞的是相對(duì)路徑,那么就是相對(duì)于Class而言的攒至,比如ClassTwo.class.getResource(name)厚者,那么傳遞的路徑就是相對(duì)于ClassTwo這個(gè)類的路徑而言。
源碼分析
上面的案例只是讓我們知道了該如何使用ClassLoader的getResource(String name)和Class的getResource(String name)方法迫吐,那么他們到底有啥區(qū)別的库菲,下面我們對(duì)源碼進(jìn)行簡(jiǎn)單的分析。
首先我們先來(lái)看一下ClassLoader的getResource(String name)的源碼志膀,這里有用到類加載機(jī)制的知識(shí)熙宇,具體參考這篇博客java類加載機(jī)制
public URL getResource(String name) {
URL url;
// 這里的parent是ExtensionClassLoader(標(biāo)準(zhǔn)擴(kuò)展類加載器)
if (parent != null) {
// 遞歸調(diào)用,第二次進(jìn)入時(shí)溉浙,parent為null
url = parent.getResource(name);
} else {
//到達(dá)系統(tǒng)啟動(dòng)類加載器
url = getBootstrapResource(name);
}
if (url == null) {
//系統(tǒng)啟動(dòng)類加載器沒(méi)有加載到烫止,遞歸回退到第一次調(diào)用然后是擴(kuò)展類加載器
url = findResource(name);
}
//最后如果都沒(méi)有加載到,雙親委派加載失敗戳稽,則加載應(yīng)用本身自己的加載器馆蠕。
return url;
}
再來(lái)看下Class的getResource(String name)方法
public java.net.URL getResource(String name) {
name = resolveName(name);
// 獲取加載該Class的ClassLoader
ClassLoader cl = getClassLoader0();
if (cl==null) {
//如果加載該Class的ClassLoader為null,則表示這是一個(gè)系統(tǒng)class
return ClassLoader.getSystemResource(name);
}
//調(diào)用ClassLoader的getResource方法
return cl.getResource(name);
}
我們看到Class的getResource先是調(diào)用了resolveName(name)
方法然后又獲取了該類的ClassLoader惊奇,再調(diào)用ClassLoader的getResource方法互躬,因此關(guān)鍵的方法就是resolveName
方法,我們看看這個(gè)方法對(duì)傳進(jìn)去的參數(shù)做了什么操作颂郎。
private String resolveName(String name) {
if (name == null) {
return name;
}
// 判斷是不是/開(kāi)頭的絕對(duì)路徑
if (!name.startsWith("/")) {
// 獲取calss信息
Class<?> c = this;
while (c.isArray()) {
c = c.getComponentType();
}
// 獲取class全路徑,在之前案例中的beanName的值為com.wsp.ClassTwo
String baseName = c.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
// 也就是com.wsp.ClassTwo變成com/wsp + / + name
name = baseName.substring(0, index).replace('.', '/')
+"/"+name;
}
} else {
// 如果是/開(kāi)頭的則去掉/
name = name.substring(1);
}
return name;
}
這個(gè)方法也就是對(duì)傳進(jìn)來(lái)的name參數(shù)進(jìn)行了改變吼渡,如果是/開(kāi)頭的則去掉/直接返回,如果不是以/開(kāi)頭的則獲取Class所在的路徑加上name返回乓序,以之前的例子為例寺酪,ClassTwo.class.getResource("");,在resolveName(String name)
中返回的結(jié)果是com/wsp/替劈。
小結(jié)
Class.getResource和ClassLoader.getResource最終都是使用ClassLoader.getResource加載資源的寄雀,不同的是Class.getResource真正調(diào)用ClassLoader.getResource方法之前,會(huì)先獲取文件的路徑(path不以'/'開(kāi)頭時(shí)抬纸,默認(rèn)是從此類所在的包下取資源咙俩;path以'/'開(kāi)頭時(shí)耿戚,則是從項(xiàng)目的ClassPath根下獲取資源)湿故。ClassLoader.getResource方法會(huì)通過(guò)雙親委派機(jī)制阿趁,先委派雙親去加載類,如果雙親沒(méi)有加載到坛猪,則再由自己加載脖阵。
參考博客:
徹底搞懂Class.getResource和ClassLoader.getResource的區(qū)別和底層原理
java類加載機(jī)制