一,简介
在JDK中,提供了以下方法来获取classpath下的资源:
public abstract class ResourceUtils { public static void main(String[] args) { test(); } /** * @param args */ public static void test() { String name = "plugins.xml"; System.out.println(null != Thread.currentThread().getContextClassLoader().getResource(name));//相对于classpath根路径 System.out.println(null != Thread.currentThread().getContextClassLoader().getResourceAsStream(name));//相对于classpath根路径 System.out.println(null != ResourceUtils.class.getResource(name));//相对于ResourceUtils.class文件所在包路径。如果name="/plugins.xml"则是相对于classpath根路径 System.out.println(null != ResourceUtils.class.getResourceAsStream(name));//相对于ResourceUtils.class文件所在包路径。如果name="/plugins.xml"则是相对于classpath根路径 } }
其中ResourceUtils.class.getResource(name),ResourceUtils.class.getResourceAsStream(name)最终还是委托给ClassLoader的getResource和getResourceAsStream去加载资源的。只不过在委托给ClassLoader加载资源之前,对资源的路径进行了一些特殊的处理。
二,ClassLoader资源加载原理
public abstract class ClassLoader { // The parent class loader for delegation private ClassLoader parent; 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; } }
众所周知,JVM中不同ClassLoader所加载资源的路径是不同的,BootstrapClassloader(根类加载器)资源加载路径为:C:\Program Files\Java\jdk1.7.0_17\jre\lib文件夹。例如:rt.jar,jce.jar等;ExtClassLoader(扩展类加载器)资源加载路径为:C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext文件夹;SystemClassLoader(系统类加载器):应用中编写的class类,引用的第三方jar包均由系统类加载器加载。
由代码中ClassLoader获取资源的优先级可知,如果不同ClassLoader加载的资源路径下存在同名的配置文件或class文件,覆盖顺序为:根类加载器 > 扩展类加载器 > 应用中的资源文件 > 应用所依赖的第三方jar (这也是JVM保证安全性的原则,否则恶意定义一个java.lang.String的类,被JVM加载是不安全的)。
例如:A工程依赖B工程,在B工程中的classpath下有个配置文件plugins.xml,同时在A工程的classpath下也有一个同名的配置文件,那么A工程下的配置文件则会覆盖B下的文件。同样包路径的class文件也会覆盖。
三,Class资源加载原理
public final class Class<T> implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { public java.net.URL getResource(String name) { name = resolveName(name);//对name进行路径特殊处理 ClassLoader cl = getClassLoader0(); if (cl==null) { // A system class. return ClassLoader.getSystemResource(name); } return cl.getResource(name);//最终还是委托给ClassLoader加载资源 } /** * 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("/")) {//如果不是绝对路径,即相对于classpath根路径 Class c = this; while (c.isArray()) { c = c.getComponentType(); } String baseName = c.getName();//取当前类路径lpp.demo.ResourceUtils int index = baseName.lastIndexOf('.');//lpp.demo if (index != -1) { name = baseName.substring(0, index).replace('.', '/') +"/"+name;//路径:lpp/demo/pluginxs.xml } } else { name = name.substring(1);//路径:pluginxs.xml } return name; } }