項目工程下讀取檔案的幾種方式。
【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;
}