天天看點

項目中不同位置資源檔案讀取的幾種方式

項目工程下讀取檔案的幾種方式。

【1】圖檔在src目錄下,即資源檔案;

項目中不同位置資源檔案讀取的幾種方式

此時測試類與圖檔位置無關

//使用getClassLoader,不加 `/`
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("getTclazz.jpg");

//這裡需要加 `/`  
InputStream inputStream = getClass().getResourceAsStream("/getTclazz.jpg");      

【2】圖檔在dao目錄下

項目中不同位置資源檔案讀取的幾種方式

① 如果測試類不在該目錄下-與檔案不同目錄

項目完整路徑:

/*不要加 `/` , /com/jdbc/dao/getTclazz.jpg 錯誤!   */
 InputStream inputStream = 
 getClass().getClassLoader().getResourceAsStream("com/jdbc/dao/getTclazz.jpg");
 
// 要加 `/`
InputStream inputStream = getClass().getResourceAsStream("/com/jdbc/dao/getTclazz.jpg");      

② 如果測試類在該目錄下-與檔案同一目錄

調用的java類檔案在該包下,【如測試類和檔案都在com.jdbc.dao包下】則可使用:

InputStream inputStream = getClass().getResourceAsStream("getTclazz.jpg");      

分析:這裡沒有加包名也沒有加"/" 。這是因為​

​InputStream java.lang.Class.getResourceAsStream(String name)​

​會根據name解析具體資源路徑。

【3】三種方式分析

上面總結執行個體了三種方式,如下所示:

  • getClass().getResourceAsStream(“getTclazz.jpg”);
  • getClass().getResourceAsStream("/com/jdbc/dao/getTclazz.jpg");**
  • getClass().getClassLoader().getResourceAsStream(“com/jdbc/dao/getTclazz.jpg”);**

第一種和第二種方式是采用Class對象去加載,第三種采用ClassLoader對象去加載資源檔案,之是以Class對象也可以加載資源檔案是因為​

​InputStream java.lang.Class.getResourceAsStream(String name)​

​​ 方法最終會調用​

​InputStream java.lang.ClassLoader.getResourceAsStream(String name)​

​​ 方法。通常我們的類加載器會使用​

​sun.misc.Launcher$AppClassLoader​

​這個類加載器。

Class類中的源碼getResourceAsStream

public InputStream getResourceAsStream(String name) {
    //這裡有名字解析操作
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name);
    }      

分析如下:

使用給定的名字找到一個資源,檢索資源的規則與目前類的類加載器ClassLoader有關。如果目前對象是被根加載器加載的,那麼将會直接調用​

​ClassLoader.getSystemResourceAsStream(name)​

​方法。但是在判斷使用哪個類加載器擷取資源前,需要根據算法來構造一個絕對路徑名字:

  • 如果name是以​

    ​/​

    ​​開頭,那麼絕對資源路徑名字就是​

    ​/​

    ​ 後面的部分;
  • 否則絕對路徑名字就是​

    ​目前類​

    ​​的包名+​

    ​/​

    ​​+name;其中包名裡面的​

    ​.​

    ​​ 被​

    ​/​

    ​替換。

注意,上面第二句裡面有個​

​目前類​

​,其實就是你調用代碼擷取資源的那個類。

String java.lang.Class.resolveName(String 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;
    }      

即,如果name是絕對路徑(帶有​

​/​

​​)那麼就移除​

​/​

​​然後使用​

​classloader.getResourceAsStream(name)​

​​來擷取流。如果name是相對路徑(不帶有​

​/​

​),那麼就添加包字首,然後使用​

​classloader.getResourceAsStream(name)​

​來擷取流。

​InputStream java.lang.ClassLoader.getResourceAsStream(String name)​

​方法如下:

//不會再次解析name
public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }      

最終擷取資源方法​

​URL java.lang.ClassLoader.getResource(String name)​

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;
}      

分析如下:

  • 首先檢視目前類加載的父類加載器是否存在,如果存在則調用父類加載器的getResource方法;
  • 如果父類加載不存在則使用根類加載器加載資源;
  • 如果url還是為null,則使用目前類加載查找資源;

看會是不是覺得就是雙親委派?聯想下類的加載過程。如果對類加載不熟悉可以參考該篇博文: 細究Java類加載機制和Tomcat類加載機制

【4】Debug分析資源在src根目錄下的檢索過程

資源圖示如下:

項目中不同位置資源檔案讀取的幾種方式

執行個體代碼

InputStream fis = EmailConfig.class.getResourceAsStream("/email.properties");      
項目中不同位置資源檔案讀取的幾種方式

① 調用Class的getResourceAsStream方法,此時name為​

​/email.properties​

項目中不同位置資源檔案讀取的幾種方式

② 解析name并擷取類加載器

解析到的name為​

​email.properties​

​​,類加載器為​

​sun.misc.Launcher$AppClassLoader@18b4aac2​

項目中不同位置資源檔案讀取的幾種方式

③ 調用URL java.lang.ClassLoader.getResource(String name)開始查找資源

項目中不同位置資源檔案讀取的幾種方式

目前類加載器為AppClassLoader,其父類加載器為ExtClassLoader,不為null則調用其父類加載器查找資源。

項目中不同位置資源檔案讀取的幾種方式

目前類加載器為ExtClassLoader,其父類加載器為null,則調用根加載器查找資源。

項目中不同位置資源檔案讀取的幾種方式
項目中不同位置資源檔案讀取的幾種方式

根加載器當然拿不到了,那麼就傳回目前類加載器ExtClassLoader并調用​​

​URL java.net.URLClassLoader.findResource(String name)​

​進行資源查找。

項目中不同位置資源檔案讀取的幾種方式

那麼會從哪些地方進行查找呢?這裡整理如下(也就是​​

​/jre/lib/ext​

​下):

[
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/access-bridge-64.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/cldrdata.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/dnsns.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/jaccess.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/jfxrt.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/localedata.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/nashorn.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunec.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunjce_provider.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunmscapi.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunpkcs11.jar,
    file: /C: /Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/zipfs.jar
]      

此時資源仍舊拿不到!這是正常的,繼續往上傳回,傳回到AppClassLoader。再次調用URLClassLoader進行資源查找:

項目中不同位置資源檔案讀取的幾種方式

路徑整理如下(tomcat/lib目錄下以及項目資源目錄下):

[
    file: /C: /Users/jane/workspace2/JavaMailDemo/build/classes/,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/annotations-api.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/catalina-ant.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/catalina-ha.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/catalina-storeconfig.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/catalina-tribes.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/catalina.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/ecj-4.6.3.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/el-api.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/jasper-el.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/jasper.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/jaspic-api.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/jsp-api.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/servlet-api.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-api.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-coyote.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-dbcp.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-i18n-es.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-i18n-fr.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-i18n-ja.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-i18n-ru.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-jdbc.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-jni.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-util-scan.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-util.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/tomcat-websocket.jar,
    file: /E: /softinstall/tomcat8.5/apache-tomcat-8.5.38/lib/websocket-api.jar,
    file: /C: /Users/jane/workspace2/JavaMailDemo/WebContent/WEB-INF/lib/javax.mail-api-1.4.4.jar,
    file: /C: /Users/jane/workspace2/JavaMailDemo/WebContent/WEB-INF/lib/slf4j-api-1.7.26.jar
]      

【5】讀取classpath下的json檔案并傳回JSONObject

/**
     * 讀取json檔案并解析為JSONObject
     * @param path--檔案儲存在classpath下的全路徑 如test.json
     */
    public static JSONObject readerJSON (String path) throws Exception {

        try {
            if (!path.startsWith("/")&&!path.startsWith("\\")){
                path="/"+path;
            }
            InputStream inputStream = ReaderJSONUtil.class.getResourceAsStream(path);
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            StringBuffer jsonBuffer = new StringBuffer();
            String line=null;
            while ((line=bufferedReader.readLine())!=null){
                jsonBuffer.append(line);
                log.debug(line);
            }
            return JSON.parseObject(jsonBuffer.toString());

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }