项目工程下读取文件的几种方式。
【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
这里使用的是fastJSON,源码实例如下:
/**
* 读取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;
}