ClassLoader可以把包(jar包,war包)内的class文件加载到JVM中,第一步就是将class文件以stream的方式读取出来。ClassLoader也将这个加载资源的方法getResourceAsStream暴露了出来。编程时可以使用这个方法来加载包内的任何资源,比如properties文件,图片等。
使用ClassLoader加载资源
当使用ClassLoader加载资源的时候,参数应该是资源文件在包内的路径,不以“/”开头,即使用绝对路径这里的绝对路径是classpath。对于下面的package结构:
如果要加载test2.properties这个文件,getResourceAsStream的参数就是“classloader/getresource/deeper/test2.properties”。
ClassLoader可以加载任何一个在classpath上存在的资源文件,可以不是一个package内,也可以不在一个包内。简单来说,只要把资源文件也当作一个类来看待,把类的全路径名中的“.”换成“/”就可以了。
getResourceAsStream的参数也可以有“..”,用来回到上一层目录。
使用Class加载资源
Class类也有一个getResourceAsStream方法。对于同一个包中的资源文件,使用Class加载资源文件会更简单。
比如上图中,如果GetResourceInCurrentPkg类想加载test.properties,只要使用下面的代码就行了。
GetResourceInCurrentPkg.
class
.getResourceAsStream(
"test.properties"
)
实际上Class类的getResourceAsStream方法内部调用了,ClassLoader类的getResourceAsStream方法。Class类的getResourceAsStream方法
源码如下:
public InputStream getResourceAsStream(String name) {
//调用resolveName方法对资源名进行解析
name = resolveName(name);
//返回当前类的ClassLoader
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
//调用当前类的ClassLoader的getResourceAsStream方法
return cl.getResourceAsStream(name);
}
再来看看 Class类resolveName方法的源码
/**
* Add a package name prefix if the name is not absolute Remove leading "/"
* if name is absolute
*/
private String resolveName(String name) {
//当资源名为null时直接返回null
if (name == null) {
return name;
}
/*
***
当资源名不以"/"开始时,对资源名进行解析,上例中的name="test.properties",经解析结果为
name="classloader/getresource/test.properties",即在前面加上了类所在包的路径。
***
*/
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;
}
参照下面的图
下面的示例代码可以成功加载资源
package classloader.getresource;
import java.io.IOException;
import java.util.Properties;
public class GetResourceInCurrentPkg {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// using Class.getResourceAsStream, the default location is the package
// of current class. It is an instance method, so every instance know
// that where is the class, and which package it is, and which class
// loader to
// use. It is a relative path.
Properties props = new Properties();
props.load(GetResourceInCurrentPkg.class
.getResourceAsStream("../getresource/test.properties"));
System.out.println(props.get("test"));
props = new Properties();
props.load(GetResourceInCurrentPkg.class
.getResourceAsStream("test.properties"));
System.out.println(props.get("test"));
props = new Properties();
props.load(GetResourceInCurrentPkg.class
.getResourceAsStream("deeper/test2.properties"));
System.out.println(props.get("test2"));
// if the path start with /, then it means the path is absolute path.
props = new Properties();
props.load(GetResourceInCurrentPkg.class
.getResourceAsStream("/classloader/getresource/deeper/test2.properties"));
System.out.println(props.get("test2"));
// Using class loader, it is always the absolute path.
props = new Properties();
props.load(GetResourceInCurrentPkg.class.getClassLoader()
.getResourceAsStream(
"classloader/getresource/deeper/test2.properties"));
System.out.println(props.get("test2"));
}
}