面試官:Class和ClassLoader的getResource方法有啥區(qū)別?
你知道的越多,不知道的就越多,業(yè)余的像一棵小草!
你來(lái),我們一起精進(jìn)!你不來(lái),我和你的競(jìng)爭(zhēng)對(duì)手一起精進(jìn)!
編輯:業(yè)余草
cnblogs.com/54chensongxia
推薦:https://www.xttblog.com/?p=5306
最近在看寫Spring的源代碼,里面有好多地方都用到了Class和ClassLoader類的getResource方法來(lái)加載資源文件。之前對(duì)這兩個(gè)類的這個(gè)方法一知半解,概念也很模糊,這邊做下整理,加深理解。

訪問(wèn)資源的主要方式
在Java中,通??梢酝ㄟ^(guò)以下方式來(lái)訪問(wèn)資源:
Class的getResource方法; ClassLoader的getResource方法; ClassLoader的getResources方法; //獲取批量資源 ClassLoader的getSystemResource; //靜態(tài)方法
在使用中,Class可通過(guò)直接引用類的class屬性而獲得,或是通過(guò)實(shí)例的 getClass() 方法來(lái)獲得。獲取ClassLoader的方式則比較多,常見以下幾種:
調(diào)用Class的getClassLoader方法,如:getClass().getClassLoader() 由當(dāng)前線程獲取ClassLoader:Thread.currentThread().getContextClassLoader() 獲取系統(tǒng)ClassLoader: ClassLoader.getSystemClassLoader()
Class.getResource 與 ClassLoader.getResource 的區(qū)別
public?class?ClassLoaderAndClassContrast?{
public?static?void?main(String[]?args)?throws?Exception?{
?Class?aClass?=?ClassLoaderAndClassContrast.class;
?ClassLoader?classLoader?=?aClass.getClassLoader();
?URL?resource?=?classLoader.getResource("cookies.properties");
?URL?resource1?=?aClass.getResource("dir/cookies.properties");
?if(resource!=null){
??byte[]?bytes?=?FileCopyUtils.copyToByteArray(resource.openStream());
?????System.out.println(new?String(bytes));
?}
}
}
兩者最大的區(qū)別,是從哪里開始尋找資源。
ClassLoader并不關(guān)心當(dāng)前類的包名路徑,它永遠(yuǎn)以classpath為基點(diǎn)來(lái)定位資源。需要注意的是在用ClassLoader加載資源時(shí),路徑不要以"/"開頭,所有以"/"開頭的路徑都返回null; Class.getResource如果資源名是絕對(duì)路徑(以"/"開頭),那么會(huì)以classpath為基準(zhǔn)路徑去加載資源,如果不以"/"開頭,那么以這個(gè)類的Class文件所在的路徑為基準(zhǔn)路徑去加載資源。
在實(shí)際開發(fā)過(guò)程中建議使用Class.getResource這個(gè)方法,但是如果你想獲取批量資源,那么就必須使用到ClassLoader的getResources()方法。
public?class?ClassLoaderAndClassContrast?{
????Class?cls?=?ClassLoaderAndClassContrast.class;
????ClassLoader?ldr?=?cls.getClassLoader();
????public?static?void?println(Object?s)?{
????????System.out.println(s);
????}
????void?showResource(String?name)?{
????????println("##?Test?resource?for:?“"?+?name?+?"”?##");
????????println(String.format("ClassLoader#getResource(\"%s\")=%s",?name,?ldr.getResource(name)));
????????println(String.format("Class#getResource(\"%s\")=%s",?name,?cls.getResource(name)));
????}
????public?final?void?testForResource()?throws?Exception?{
????????showResource("");
????????//對(duì)于Class來(lái),返回這個(gè)類所在的路徑
????????showResource("/");
????????showResource(cls.getSimpleName()?+?".class");
????????String?n?=?cls.getName().replace('.',?'/')?+?".class";
????????showResource(n);
????????showResource("/"?+?n);
????????showResource("java/lang/Object.class");
????????showResource("/java/lang/Object.class");
????}
????public?static?void?main(String[]?args)?throws?Exception?{
????????println("java.class.path:?"?+?System.getProperty("java.class.path"));
????????println("user.dir:?"?+?System.getProperty("user.dir"));
????????println("");
????????ClassLoaderAndClassContrast?t?=?new?ClassLoaderAndClassContrast();
????????t.testForResource();
????}
}
正確使用getResource方法
避免使用 Class.getResource("/") 或 ClassLoader.getResource("")。你應(yīng)該傳入一個(gè)確切的資源名,然后對(duì)輸出結(jié)果作計(jì)算。比如,如果你確實(shí)想獲取當(dāng)前類是從哪個(gè)類路徑起點(diǎn)上執(zhí)行的,以前面提到的test.App來(lái)說(shuō),可以調(diào)用 App.class.getResource(App.class.getSimpleName() + ".class")。如果所得結(jié)果不是 jar 協(xié)議的URL,說(shuō)明 class 文件沒(méi)有打包,將所得結(jié)果去除尾部的 "test/App.class",即可獲得 test.App 的類路徑的起點(diǎn);如果結(jié)果是 jar 協(xié)議的 URL,去除尾部的 "!/test/App.class",和前面的 "jar:",即是 test.App 所在的 jar 文件的url。 如果要定位與某個(gè)類同一個(gè)包的資源,盡量使用那個(gè)類的getResource方法并使用相對(duì)路徑。如前文所述,要獲取與 test.App.class 同一個(gè)包下的 App.js 文件,應(yīng)使用 App.class.getResource("App.js") 。當(dāng)然,事無(wú)絕對(duì),用 ClassLoader.getResource("test/App.js") 也可以,這取決于你所面對(duì)的問(wèn)題是什么。 如果對(duì)ClassLoader不太了解,那就盡量使用Class的getResource方法。 如果不理解或無(wú)法確定該傳給Class.getResource 方法的相對(duì)路徑,那就以類路徑的頂層包路徑為參考起點(diǎn),總是傳給它以 "/" 開頭的路徑吧。 不要假設(shè)你的調(diào)試環(huán)境就是最后的運(yùn)行環(huán)境。你的代碼可能不打包,也可能打包,你得考慮這些情況,不要埋坑。
獲取批量資源
使用classLoader的getResources方法可以獲得批量資源
ClassLoader?classLoader?=?Thread.currentThread().getContextClassLoader();
Enumeration?resources?=?classLoader.getResources("META-INF/MANIFEST.MF");
Spring的ResourceLoader
在Spring框架中ResourceLoader和ResourcePatternResolver接口封裝了獲取資源的方法,我們可以直接拿來(lái)使用。ResourceUtils這個(gè)類中提供了很多判斷資源類型的工具方法,可以直接使用。
//前面三種的寫法效果是一樣的,必須從classpath基準(zhǔn)目錄開始寫精確的匹配路徑
//如果想匹配多個(gè)資源,需要以classpath*/打頭,然后配合通配符使用
ClassPathXmlApplicationContext?applicationContext?=?new?ClassPathXmlApplicationContext("beans.spring.xml");
ClassPathXmlApplicationContext?applicationContext?=?new?ClassPathXmlApplicationContext("/beans.spring.xml");
ClassPathXmlApplicationContext?applicationContext?=?new?ClassPathXmlApplicationContext("classpath:/beans.spring.xml");
ClassPathXmlApplicationContext?applicationContext?=?new?ClassPathXmlApplicationContext("classpath*:/**/beans.spring.xml");
ResourceLoader 接口 ResourcePatternResolver 接口 --- 重點(diǎn)
給出一個(gè)例子
String?txt?=?"";
ResourcePatternResolver?resolver?=?new?PathMatchingResourcePatternResolver();
Resource[]?resources?=?resolver.getResources("templates/layout/email.html");
Resource?resource?=?resources[0];
//獲得文件流,因?yàn)樵趈ar文件中,不能直接通過(guò)文件資源路徑拿到文件,但是可以在jar包中拿到文件流
InputStream?stream?=?resource.getInputStream();
StringBuilder?buffer?=?new?StringBuilder();
byte[]?bytes?=?new?byte[1024];
try?{
????for?(int?n;?(n?=?stream.read(bytes))?!=?-1;?)?{
????????buffer.append(new?String(bytes,?0,?n));
????}
}?catch?(IOException?e)?{
????e.printStackTrace();
}
txt?=?buffer.toString();
ResourceUtils ResourcePatternUtils
評(píng)論
圖片
表情
