前言
在Web开发中经常要加载项目下的各种资源,有一种方法是用ClassLoader或者Class类提供的getResource来加载。本文将从代码上来分析两者的区别
正文
经常看到下面类似的代码,往往很容易搞混
public static void loadSourceWithClassLoader() {
SysFilePath.class.getResource("/org/easyutil/resources/utf8.txt"); //从classpath下加载资源
SysFilePath.class.getResource("utf8.txt");//从当前目录下加载资源
SysFilePath.class.getClassLoader().getResource("org/easyutil/resources/utf8.txt");//从classpath下加载资源
}
- important note:这里需要记住一点的就是class.getResource使用ClassLoader类中的getResource实现的。也就是说两者的本质都是使用加载器来加载资源
加载器种类很多,在java项目有下面三种加载器
1)Bootstrap ClassLoader
2)Extension ClassLoader
3)App ClassLoader
如果是Web的项目且使用的是Tomcat容器,那么加载器还包括
4)WebAppClassLoader
不同的加载器都有一个指定的加载目录,自己理解为是classpath.所以ClassLoader.getResource(path)从classpath中加载path路径下的资源,这里path是开头不能是“/”且其后按照/分割的格式
从上面的可以知道了 SysFilePath.class.getClassLoader().getResource("org/easyutil/resources/utf8.txt");
加载的就是classpath路径下的资源,具体实现代码如下
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;
}
从加载器继承体系的最上层查找资源,如果找不到资源在一层层下来直到使用当前加载器(默认加载目录)加载资源(findResource)
再看看Class类中的getResource怎么加载资源的.其实开头是否要加“/”是业务要求导致的(这个类的功能需求决定的),而本质是使用ClassLoader来加载资源,它要求路径开头不需要“/”.这么说来开头是否要“/”会产生不同的代码逻辑处理,我们看看这些不同逻辑是怎么处理的
Class类的getResource
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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
最后获取当前的ClassLoader加载资源,那么这里的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(外面指定的资源路径),这样直接定位到classpath下当前所在的路径下的指定name的路径中的资源
如果以“/”开头则,直接去掉“/”,表示定位到classpath下的name路径所在的资源
最后总结得出下面结论
class.getResource有下面两个算法
- 如果以“/”开头则加载classpath下的资源
如果不以“/”开头则加载当前目录下的资源
ClassLoader.getResource算法
从classpath加载资源,且开头不能是“/”其后按照“/”分割的路径(相对路径)