ClassLoader可以把包(jar包,war包)内的class文件加载到JVM中,第一步就是将class文件以stream的方式读取出来。ClassLoader也将这个加载资源的方法getResourceAsStream暴露了出来。编程时可以使用这个方法来加载包内的任何资源,比如properties文件,图片等。
使用CL加载资源
当使用ClassLoader加载资源的时候,参数应该是资源文件在包内的路径,不以“/”开头。对于下面的package结构:
如果要加载test.properties。getResourceAsStream的参数就是“classloader/getresource/deeper/test2.properties”。
CL可以加载任何一个在classpath上存在的资源文件,可以不是一个package内,也可以不在一个包内。简单来说,只要把资源文件也当作一个类来看待,把类的全路径名中的“.”换成“/”就可以了。
getResourceAsStream的参数也可以有“..”,用来回到上一层目录。
使用Class加载资源文件
Class类也有一个getResourceAsStream方法。对于同一个包中的资源文件,使用Class加载资源文件会更简单。
比如上图中,如果GetResourceInCurrentPkg类想加载test.properties,只要使用下面的代码就行了。
GetResourceInCurrentPkg.class.getResourceAsStream("test.properties")
Class类也是使用CL去加载资源的。它所做的事情就是将参数修订成CL需要的格式。变化都在Class类的resolveName方法中。
/** * Add a package name prefix if the name is not absolute Remove leading "/" * if name is absolute */ 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; }
方法含义很简单
- 对于不是以“/”开头的参数,就认为这个路径是相对于当前类的路径,所以加载当前包中的资源只要写资源文件的名字就可以了。方法最大的一块代码就是为了得到当前类的路径,然后和传进来的相对路径参数拼起来,作为资源文件的绝对路径丢给CL。
- 对于以“/”开头的参数,就认为参数是资源文件的绝对路径,将这个斜线去掉之后,丢给CL去加载就行了。
同样,这个方法的参数同样接受“..”来返回上一层目录。
例程
参照下面的图
下面的示例代码可以成功加载资源
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")); } }