處理配置文件對(duì)于Java程序員來(lái)說(shuō)再常見(jiàn)不過(guò)了,不管是Servlet,Spring,抑或是Structs,都需要與配置文件打交道。Java將配置文件當(dāng)作一種資源(resource)來(lái)處理,并且提供了兩個(gè)類(lèi)來(lái)讀取這些資源,一個(gè)是Class類(lèi),另一個(gè)是ClassLoader類(lèi)。
當(dāng)我們自己的程序需要處理配置文件時(shí)(比如xml文件或properties文件),通常會(huì)遇到兩個(gè)問(wèn)題:
(1)我的配置文件應(yīng)該放在哪里?
(2)怎么我的配置文件找不到了?
在了解了Java加載資源文件的機(jī)制后,以上這兩個(gè)問(wèn)題便迎刃而解了。
對(duì)于第一個(gè)問(wèn)題,答案是:請(qǐng)將你的資源文件放在classpath里,如果資源文件在jar中,請(qǐng)將該jar文件也加到classpath里面。
對(duì)于第二個(gè)問(wèn)題,就得看你是使用的是哪個(gè)類(lèi)(Class還是ClassLoader)來(lái)加載資源文件了,所以接下來(lái)分別討論一下Class類(lèi)和ClassLoader類(lèi)對(duì)于資源文件的加載機(jī)制。
(一)用Class類(lèi)加載資源文件
通過(guò)調(diào)用Class類(lèi)的getResourceAsStream方法來(lái)加載資源文件:
public InputStream getResourceAsStream(String pathToConfigFile);
該方法接收一個(gè)String類(lèi)型的參數(shù)(pathToConfigFile)來(lái)表示資源文件的地址,如果加載成功,則返回該資源文件的輸入流(InputStream),如果失敗,則返回null。重要的是,在傳入pathToConfigFile參數(shù)時(shí),有兩種方式,第一種方式為絕對(duì)定位方式,即pathToConfigFile以"/"開(kāi)頭,此時(shí)Java以classpath為根目錄,直接加上pathToConfigFile來(lái)搜索資源文件。第二種方式為相對(duì)定位方式,即pathToConfigFile不以"/"開(kāi)頭,此時(shí)資源文件的全路徑應(yīng)該為:調(diào)用getResourceAsStream方法的類(lèi)的package路徑加上pathToConfigFile。(在將package轉(zhuǎn)為目錄時(shí)將"."變成"/")
舉個(gè)例子,在IntelliJ Idea中創(chuàng)建一個(gè)java工程,目錄結(jié)構(gòu)如下:

該工程里有兩個(gè)resources文件夾,一個(gè)位于davenkin文件夾下,一個(gè)直接位于src文件夾下。第一個(gè)resources文件夾下有一個(gè)config.properties文件,其內(nèi)容為:
name = ConfigUnderDavenkin |
第二個(gè)resources文件夾下也有一個(gè)config.properties文件,其內(nèi)容為:
在davenkin包下定義ResourceLoader.java來(lái)加載資源文件:
package davenkin; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class ResourceLoader { public static void main(String[] args) throws IOException { ResourceLoader resourceLoader = new ResourceLoader(); resourceLoader.loadProperties1(); } public void loadProperties1() throws IOException { InputStream input = null; try { input = Class.forName("davenkin.ResourceLoader").getResourceAsStream("/resources/config.properties"); //also can be this way: //input = this.getClass().getResourceAsStream("/resources/config.properties"); } catch (ClassNotFoundException e) { e.printStackTrace(); } printProperties(input); } private void printProperties(InputStream input) throws IOException { Properties properties = new Properties(); properties.load(input); System.out.println(properties.getProperty("name")); } } |
輸出結(jié)果為第二個(gè)resources文件夾下config.properties的內(nèi)容:
原因在于(請(qǐng)注意ReourceLoader.java文件中的紅色部分):我們給出的資源文件路徑(/resources/config.properties)以"/"開(kāi)頭,即使用的是絕對(duì)定位方式,所以找到的是直接在classpath下的resources文件夾。如果去掉資源文件文件路徑前的"/",則采用的是相對(duì)定位方式,此時(shí)應(yīng)該輸出davenkin/resources/config.properties文件的內(nèi)容。
(二)用ClassLoader類(lèi)加載資源文件
ClassLoader類(lèi)也提供和Class類(lèi)相同的加載方法:
public InputStream getResourceAsStream(String pathToConfigFile);
用ClassLoader加載配置文件時(shí),pathToConfigFile均不能以"/"開(kāi)頭,在查找時(shí)直接在classpath下進(jìn)行查找。Class類(lèi)在查找資源文件時(shí),也是代理(delegate)給ClassLoader完成查找功能的,請(qǐng)參考Java官方文檔。
在使用Class和ClassLoader加載資源文件時(shí),有幾種區(qū)別細(xì)微的方法,修改ResourceLoader.java文件如下:
package davenkin; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class ResourceLoader { public static void main(String[] args) throws IOException { ResourceLoader resourceLoader = new ResourceLoader(); resourceLoader.loadProperties1(); resourceLoader.loadProperties2(); resourceLoader.loadProperties3(); resourceLoader.loadProperties4(); resourceLoader.loadProperties5(); resourceLoader.loadProperties6(); } public void loadProperties1() throws IOException { InputStream input = null; try { input = Class.forName("davenkin.ResourceLoader").getResourceAsStream("/resources/config.properties"); } catch (ClassNotFoundException e) { e.printStackTrace(); } printProperties(input); } public void loadProperties2() throws IOException { InputStream input = null; input = this.getClass().getResourceAsStream("/resources/config.properties"); printProperties(input); } public void loadProperties3() throws IOException { InputStream input = this.getClass().getResourceAsStream("resources/config.properties"); printProperties(input); } public void loadProperties4() throws IOException { InputStream input = this.getClass().getClassLoader().getResourceAsStream("resources/config.properties"); printProperties(input); } public void loadProperties5() throws IOException { InputStream input = ClassLoader.getSystemResourceAsStream("resources/config.properties"); printProperties(input); } public void loadProperties6() throws IOException { InputStream input = ClassLoader.getSystemClassLoader().getResourceAsStream("resources/config.properties"); printProperties(input); } private void printProperties(InputStream input) throws IOException { Properties properties = new Properties(); properties.load(input); System.out.println(properties.getProperty("name")); } } |
以上程序輸出結(jié)果為(請(qǐng)仔細(xì)揣摩,稍不小心(比如多加了一個(gè)"/"或少加了一個(gè)"/"),就會(huì)報(bào)NullPointerException異常,表明你的資源文件沒(méi)有找到):
ConfigUnderSrc ConfigUnderSrc ConfigUnderDavenkin ConfigUnderSrc ConfigUnderSrc ConfigUnderSrc |