Java中资源文件获取源码浅析
Java中资源文件获取源码浅析
文章目录
环境:JDK 1.8.0_191 JDK11.0.10
话不多说,上代码:
package com.snails.test;
import java.net.URL;
public class OperatorsTest {
public static void main(String[] args) {
URL resource = OperatorsTest.class.getResource("jdbc.properties");
String path = resource.getPath();
System.out.println(path);
}
}
JDK11
Class.getResource(String)
首先进 Class.getResource 方法中看看:
public URL getResource(String name) {
// 判断 name 是不是以/开头,
// 1.如果不是则获取当前类(OperatorsTest)的包名,并将包名的.替换为/然后和name拼接 eg name = "com/snails/test" + "/" + name
// 2. 如果是则name = substring(1) 去掉/
name = resolveName(name);
// 获取module
Module thisModule = getModule();
// 如果module是已命名的module
if (thisModule.isNamed()) {
// check if resource can be located by caller
// 判断是不是.class文件或包名
if (Resources.canEncapsulate(name)
&& !isOpenToCaller(name, Reflection.getCallerClass())) {
return null;
}
// resource not encapsulated or in package open to caller
String mn = thisModule.getName();
// 获取类加载器AppClassLoader
ClassLoader cl = getClassLoader0();
try {
if (cl == null) {
return BootLoader.findResource(mn, name);
} else {
// 在父类加载器中查找资源文件,如果没找到则去BuiltinClassLoader类加载器路径中找
return cl.findResource(mn, name);
}
} catch (IOException ioe) {
return null;
}
}
// unnamed module
// 获取AppClassLoader类加载器
ClassLoader cl = getClassLoader0();
if (cl == null) {
return ClassLoader.getSystemResource(name);
} else {
// 查找父类加载器路径中的资源文件是存在,不存在则去BuiltinClassLoader类加载器路径中查找
// 通过name拿到module,如果不在模块包中,但可能在为此加载器定义的模块中
// 查看资源的缓存是否存为空,为空则在所有模块中搜索资源
return cl.getResource(name);
}
}
从上面分析可以得到 Class.getResource(String) 方法首先会去当前模块的当前类所属包下去找资源文件,如果找不到则去其他模块同名包下找。
Class.getResourceAsStream(String) 查找流程和 Class.getResource(String) 方法是一样的,这里就不分析了,不同的是会将 URL 转化为 InputStream。
Class.getClassLoader().getResource(String)
public URL getResource(String name) {
Objects.requireNonNull(name);
URL url;
if (parent != null) {
// 去父类加载器路径中找
url = parent.getResource(name);
} else {
// 去 jdk 的module中找
url = BootLoader.findResource(name);
}
if (url == null) {
// 先去 jdk module中找,找不到然后去classpath中找
url = findResource(name);
}
return url;
}
可以看到 Class.getClassLoader().getResource(String) 方法会先去 jdk 的 module 中找,找不到再去编译后的项目路径中找,所以这里的传参相对 Class.getResource(String) 更加灵活,可以查找项目中任意给定路径的资源。如查找 com.snails.resource 包下的 jdbc.properties:
URL resource = OperatorsTest.class.getClassLoader().getResource("com/snails/resource/jdbc.properties");
Class.getClassLoader().getResourceAsStream(String) 查找和 Class.getClassLoader().getResource(String) 一样,这里就不分析了,只不过将 URL 转为了 InputStream。
JDK 8
Class.getResource(String)
public java.net.URL getResource(String name) {
// 判断 name 是不是以/开头,
// 1.如果不是则获取当前类(OperatorsTest)的包名,并将包名的.替换为/然后和name拼接 eg name = "com/snails/test" + "/" + name
// 2. 如果是则name = substring(1) 去掉/
name = resolveName(name);
// 获取类加载器AppClassLoader
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResource(name);
}
return cl.getResource(name);
}
ClassLoader
public URL getResource(String name) {
URL url;
if (parent != null) {
// 去父类加载器路径中查找
url = parent.getResource(name);
} else {
// 去 jdk/jre/lib和jdk/jre/classes 中查找
url = getBootstrapResource(name);
}
if (url == null) {
//父类中,没找到则去URLClassLoader类加载器路径中查找 根据项目编译后的路径+资源文件名
url = findResource(name);
}
return url;
}
所以资源是放在OperatorsTest类所属的包中则 Class.getResource(String) 的资源名不用带上包名:
URL resource = OperatorsTest.class.getResource("jdbc.properties");
如果资源是放在其它包中(如 com.snails.resource )那是不行的,resolveName 方法会给你拼接成这样:
com/snails/test/com/snails/resource
所以 Class.getResource(String) 只适合以下情况:
-
查找资源和调用类同包
-
jdk/jre/lib或jdk/jre/classes下的包中有这个配置,比如自己手动打包一个jar,里面资源路径如下:
getResource(“jdbc.properties”) :com/snails/test/jdbc.properties
getResource(“com/snails/resource/jdbc.properties”) : com/snails/test/com/snails/resource/jdbc.properties
Class.getResourceAsStream(String) 方法和 Class.getResource(String) 方法查找过程是一样的,这里就不分析了,只不过将 URL 转为了 InputStream。
Class.getClassLoader().getResource(String)
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;
}
Class.getClassLoader().getResource(String) 和 Class.getResource(String) 差异点在于 多了个 resolveName(name) 步骤,所以Class.getClassLoader().getResource(String) 的灵活性就更强,可以查找项目中任意给定路径的资源。如查找 com.snails.resource包下的 jdbc.properties:
URL resource = OperatorsTest.class.getClassLoader().getResource("com/snails/resource/jdbc.properties");
Class.getClassLoader().getResourceAsStream(String) 查找和 Class.getClassLoader().getResource(String) 一样,这里就不分析了,只不过将 URL 转为了 InputStream。
总结
不论 jdk8 还是 jdk11 可以看到两者查找资源方式都是大同小异的,只不过 jdk11 模块化后是去模块中找,jdk8 则是去 jdk/jre/lib,jdk/jre/classes 中找
- Class.getResource(String)/Class.getResourceAsStream(String) 查找当前包下和 jdk 环境中的资源
- Class.getClassLoader().getResource(String)/Class.getClassLoader().getResourceAsStream(String) 查找项目指定路径下的资源
本文来自博客园,作者:SnailsH,转载请注明原文链接:https://www.cnblogs.com/SnailsWalk/p/17976247