项目结构
1 | │ |
读取方式
1 | public class ResourceTest { |
总结规律如下:
Class.getResource()
的资源获取如果以/
开头, 则从根路径开始搜索资源Class.getResource()
的资源获取如果不以/
开头, 则从当前类所在的路径开始搜索资源ClassLoader.getResource()
的资源获取不能以/
开头, 统一从根路径开始搜索资源
源码分析
首先看一下Class
类中的两个方法实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}
public java.net.URL getResource(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResource(name);
}
return cl.getResource(name);
}
可以看到, Class
类先通过resoveName
方法解析出资源文件路径, 然后委托ClassLoader
去加载资源的, 首先看一下resolveName
方法的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20private 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;
}
核心逻辑就是
- 如果资源路径入参以
/
开头则截取/
后面的内容作为资源路径 - 否则通过截取当前类的全限定名称获取当前类所在的包, 然后将包分隔符
.
替换为/
, 例:com.test.ResourceTest
->com/test/
, 最后拼接上资源路径入参作为资源文件的路径
再看一下ClassLoader
的两个方法实现
1 | public URL getResource(String name) { |
getResourceAsStream
方法也是先调用同类的getResource
方法, 所以重点看一下getResource
方法的实现. 该方法首先使用了双亲委派机制来加载资源, 最终的父类ClassLoader
使用getBootstrapClassPath
去加载资源, 如果父类没加载到还会findResource
去加载, 因此ClassLoader
子类可以通过覆盖findResource
方法来实现自定义的资源加载实现.
本示例中就是通过AppClassLoader
的 findResource
方法(实际是继承自URLClassLoader
)加载到的资源, 该方法在URLClassLoader
中实现如下1
2
3
4
5
6
7
8
9
10
11
12
13public URL findResource(final String name) {
/*
* The same restriction to finding classes applies to resources
*/
URL url = AccessController.doPrivileged(
new PrivilegedAction<URL>() {
public URL run() {
return ucp.findResource(name, true);
}
}, acc);
return url != null ? ucp.checkURL(url) : null;
}