關於getSystemResource, getResource 的總結


項目中, 有時候要讀取當前classpath下的一些配置文件. 之前用的讀取配置文件的代碼如下

    public static Properties loadPropertiesFile(String fileName){
            Properties prop = new Properties();
        
            InputStream inStream = ClassLoader.getSystemResourceAsStream(fileName);
            if(null!=inStream){
                try {
                    prop.load(inStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return prop;
    }

 

使用的方式是 ClassLoader.getSystemResourceAsStream(fileName)獲取這個fileName對應的properties文件的輸入流, 然后用prop對象的load方法. 用在生產環境的jstorm中一切正常, 但是切換到測試環境的jstorm后發現inStream總是null.

后來改使用如下方式, 在不同環境下都能正常使用了:

    public static Properties loadPropertiesFile(String fileName){
        Properties prop = new Properties();

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        InputStream inStream = classLoader.getResourceAsStream(fileName);
        if(null!=inStream){
            try {
                prop.load(inStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return prop;
    }

從當前線程獲取到載入這個類的classloader, 然后再通過這個classloader來獲取配置文件的InputStream. 到這里還是很不解,為啥會有這樣的問題.於是從網上眾多方法中選出幾個來分析一下.

下面是測試代碼,嘗試讀取程序跟目錄的一個叫business.properties文件. 這個類是放在tomcat里運行的Restlet, 可以看成是一個簡單restful接口. 除此之外我還加了個main方法, 用來不是在tomcat的環境里運行,而是直接運行常規Java程序那樣運行的.

@Path(value = "/test")
public class TestCode {
    public static void main(String[] args){
        TestCode tc = new TestCode();
        tc.testcode();

    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path(value = "getPath")
    public String testcode(){
     //方法1 URL url1
= ClassLoader.getSystemResource("business.properties"); System.out.println("url1:\t" + (url1 == null ? "null" : url1.getPath())); URL url1withSlash = ClassLoader.getSystemResource("/business.properties"); System.out.println("url1/:\t" + (url1withSlash == null ? "null" : url1withSlash.getPath()));     

//方法2 ClassLoader classLoader2
= this.getClass().getClassLoader(); URL url2 = classLoader2.getResource("business.properties"); System.out.println("url2:\t" + (url2 == null ? "null" : url2.getPath())+";classLoader is:"+classLoader2.toString()); URL url2withSlash = classLoader2.getResource("/business.properties"); System.out.println("url2/:\t" + (url2withSlash == null ? "null" : url2withSlash.getPath()));
//方法3 URL url3
= this.getClass().getResource("business.properties"); System.out.println("url3:\t" + (url3 == null ? "null" : url3.getPath())); URL url3withSlash = this.getClass().getResource("/business.properties"); System.out.println("url3/:\t" + (url3withSlash == null ? "null" : url3withSlash.getPath()));
//方法4 ClassLoader classLoader4
= Thread.currentThread().getContextClassLoader(); URL url4 = classLoader4.getResource("business.properties"); System.out.println("url4:\t" + (url4 == null ? "null" : url4.getPath())+";classLoader is:"+classLoader4.toString()); URL url4withSlash = classLoader4.getResource("/business.properties"); System.out.println("url4/:\t" + (url4withSlash == null ? "null" : url4withSlash.getPath())); return "OK"; } }

 

下面看下通過tomcat運行和直接運行main方法輸出內容的區別.

通過main方法,直接運行程序,輸出結果是:

 

url1: /D:/thomas-dev/Team-Building-master/target/classes/business.properties
url1/: null
url2: /D:/thomas-dev/Team-Building-master/target/classes/business.properties;classLoader is:sun.misc.Launcher$AppClassLoader@7d05e560
url2/: null
url3: null
url3/: /D:/thomas-dev/Team-Building-master/target/classes/business.properties
url4: /D:/thomas-dev/Team-Building-master/target/classes/business.properties;classLoader is:sun.misc.Launcher$AppClassLoader@7d05e560
url4/: null

 

通過tomcat運行, 然后訪問這個restful接口, 執行的結果是:

url1: null
url1/: null
url2: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties;classLoader is:WebappClassLoader
context: ROOT
delegate: false
----------> Parent Classloader:
java.net.URLClassLoader@1ae8873a

url2/: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties
url3: null
url3/: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties
url4: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties;classLoader is:WebappClassLoader
context: ROOT
delegate: false
----------> Parent Classloader:
java.net.URLClassLoader@1ae8873a

url4/: /D:/apache-tomcat-8.0.33/webapps/ROOT/WEB-INF/classes/business.properties

 

經過查詢資料得知:

方法1:使用的是jvm的ClassLoader, 如果是直接運行的Java程序, 那么的確是調用jvm的ClassLoader, 於是調用的程序的根目錄是可以獲取這個文件的. 而在tomcat中,這個類並不是由系統自帶的ClassLoader裝載的, tomcat中而是由一個叫WebappClassLoader來裝載的, jvm的ClassLoader取到的這文件是null

方法2: 獲取加載當前類的ClassLoader, 這個ClassLoader會隨着環境的變化而變化, 可以看到第一次直接運行的ClassLoader是jvm自帶的classLoader is:sun.misc.Launcher$AppClassLoader@7d05e560, 而在tomcat中是WebappClassLoader,它的父級是java.net.URLClassLoader@1ae8873a, 都能獲取到properties文件, 所以方法2是可行的.

方法3:直接從當前對象的類調用getResource方法, 剛進去就調用了resolveName方法, 這個resolveName方法,判斷文件路徑是以/ 開頭還是./開頭, 來確定是相對與當前class文件目錄還是相對於程序根目錄, 然后也會像方法2那樣獲取加載當前類的ClassLoader, 如果獲取不到則使用系統自帶的ClassLoader.. 這個方法比較智能,可以使用,但是跟其他方法相比, 需要加一個 /表示程序根目錄.

方法4: 獲取加載當前線程的ClassLoader,當前類是在當前線程調用的,則他們的ClassLoader對象是一樣的, 可以看他們輸出的ClassLoader的內存地址是一樣的. 

 

本次了解得也還是比較淺顯,有一些深入問題沒搞清楚, 以后深入了解ClassLoader之后再補充..


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM