获取外部资源的几种方式
1.使用FileInputStream(不推荐)
使用FileInputStream要传入File对象,此处便于演示,将输出配置文件的位置。
代码
InputStream is = new FileInputStream(new File("ReflectDemo/resource/pro.properties"));
System.out.println(new File("ReflectDemo/resource/pro.properties").getPath());
结果
ReflectDemo\resource\pro.properties
注:使用此方法获取获取配置文件要指定文件的绝对路径/相对路径。并且获取到的是在项目根目录下或盘符下的配置文件,对于出现在classpath(类路径)下的配置文件,FileInputStream是读不到的。
2.使用Class.getResourceAsStream()方法
此处便于演示,将调用Class.Resource().getPath()来输出文件位置
代码
System.out.println(ReflectDemo.class.getResource("/pro.properties").getPath());
结果
/D:/idea/object/out/production/ReflectDemo/pro.properties
此方法可以用于获取类路径的配置文件。
3.Class.getClassLoder().getResourceAsStream()方法
此处便于演示,将调用Class.getClassLoder().Resource().getPath()来输出文件位置
代码
System.out.println(ReflectDemo.class.getClassLoader().getResource("pro.properties").getPath());
结果
/D:/idea/object/out/production/ReflectDemo/pro.properties
以上两方法区别
Class.getResourceAsStream(String name) 其实调用的就是
Class.getClassLoder().getResourceAsStream(String name)。
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);
}
调用之前多了一个用于解析name的操作,如果开头是“/”则跟直接调用Class.getClassLoder().getResourceAsStream(String path)一样
例:Class.getClassLoder().getResourceAsStream("");==Class.getResourceAsStream("/");
如果Class.getResourceAsStream(String name);开头不是"/",则Class.resolveName(name)方法将会返回此类文件的目录+name
也就是说此方法获取的Resource是在此类目录下的并非classpath根目录。
他们最终都会调用ClassLoder.getResource(String name);
这个方法通过递归实现,双亲委派原理。
源代码
/**
* ...
* @param name
* The resource name
*
* @return A URL object for reading the resource, or
* null if the resource could not be found or the invoker
* doesn't have adequate privileges to get the resource.
*
* @since 1.1
**/
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;
}
"该方法将首先在父类加载器中搜索资源; 如果父类加载器为null,则搜索虚拟机内置的类加载器的路径。 那个方法失败,将调用findResource(String)来查找资源。"
简单的说 就是第一次执行进去首先迭代进入最顶层的启动类加载器(Bootstrap ClassLoader)如果找不到资源,就会返回null ,并跳出去执行它子加载器(Extension ClassLoader)重写的findResource方法,如果返回null就继续跳出执行下一个子加载器(App ClassLoader)的findResoure方法,如果继续返回空的话(在没有自定义类加载器的情况下,就会直接返回 null。有的话,继续执行自定义类加载器的findResource方法)。这个方法会返回resource的URL对象 或者 nul。
对于喜欢"钻牛角尖"的小白,可能对于上面出现的 双亲委派,Bootstrap ClassLoader,Extension ClassLoader,App ClassLoader术语很困扰。请点击链接进行详细了解。
4.ClassLoader.getSystemResource()方法
相信眼睛尖的小伙伴已经发现了Class.getResourceAsStream()源代码中出现了这个方法。其实正如此方法所言,就是通过调用系统类加载器(App ClassLoader)的getResource()方法,和前两种方法没有什么本质上的区别。
总结
1.一般不推荐通过FileInputStream 来获取资源文件
1.现在的web项目,resources编译后就在classes目录下,通过classloader可以直接获取
2.对于变动的环境(现在都是部署在linux机器上)来说不太适用。
2.class.getResource("/") == class.getClassLoader().getResource("")
其实,Class.getResource和ClassLoader.getResource本质上是一样的,都是使用ClassLoader.getResource加载资源的。
3.Class.getResource真正调用ClassLoader.getResource方法之前,会先获取文件的路径(path不以'/'开头时,默认是从此类所在的包下取资源;path以'/'开头时,则是从项目的ClassPath根下获取资源)。
4.ClassLoader.getResource方法会通过双亲委派机制,先委派双亲去加载类,如果双亲没有加载到,则再由自己加载。
参考链接&进一步阅读
https://blog.csdn.net/zhangshk_/article/details/82704010
https://blog.csdn.net/eff666/article/details/52203406
物以类聚 人以群分
菅江晖