最近在工作中有用到资源加载、发现 Class
和 ClassLoader
都可以加载 classPath
下的文件
对比一下他们之间的异同

package com.demo.test.get;
public class GetResourceMain {
public static void main(String[] args) {
Class<GetResourceMain> getResourceMainClass = GetResourceMain.class;
ClassLoader classLoader = getResourceMainClass.getClassLoader();
System.out.println("classGetResource:" + getResourceMainClass.getResource(""));
System.out.println("classLoaderGetResource:" + classLoader.getResource(""));
System.out.println("/ classGetResource:" + getResourceMainClass.getResource("/"));
System.out.println("/ classLoaderGetResource:" + classLoader.getResource("/"));
System.out.println("logback.xml classGetResource:" + getResourceMainClass.getResource("logback.xml"));
System.out.println("logback.xml classLoaderGetResource:" + classLoader.getResource("logback.xml"));
System.out.println("/logback.xml classGetResource:" + getResourceMainClass.getResource("/logback.xml"));
System.out.println("/logback.xml classLoaderGetResource:" + classLoader.getResource("/logback.xml"));
System.out.println("jar inner file classLoaderGetResource:" + classLoader.getResource("META-INF/maven/org.slf4j/slf4j-api/pom.xml"));
}
}
classGetResource:file:/Users/xxxx/IdeaProjects/logbackTest/target/classes/com/demo/test/get/
classLoaderGetResource:file:/Users/xxxx/IdeaProjects/logbackTest/target/classes/
/ classGetResource:file:/Users/xxxx/IdeaProjects/logbackTest/target/classes/
/ classLoaderGetResource:null
logback.xml classGetResource:null
logback.xml classLoaderGetResource:file:/Users/xxxx/IdeaProjects/logbackTest/target/classes/logback.xml
/logback.xml classGetResource:file:/Users/xxxx/IdeaProjects/logbackTest/target/classes/logback.xml
/logback.xml classLoaderGetResource:null
jar inner file classGetResource:jar:file:/Users/xxxx/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/META-INF/maven/org.slf4j/slf4j-api/pom.xml
jar inner file classLoaderGetResource:jar:file:/Users/xxxx/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/META-INF/maven/org.slf4j/slf4j-api/pom.xml
通过日志分析我们得知 Class#getResouce
如果传入的参数以 /
开头的话则获取到的是当前 classpath
的绝对路径。如果不是的话、则是获取到当前 class
所在的路径
ClassLoader#getResource
则默认获取到的就是 classpath
下的路径、如果传入的参数以 /
开头则返回 null
ClassLoader#getResource
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;
}
双亲委派机制、先给父类加载器中尝试加载、然后再到自己的。
Class#getResource
最终还是调用 ClassLoader
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);
}
这个关键在于对 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;
}
如果是以 /
开头、则直接去掉 /
然后返回
如果不是则获取当前 class
的全路径、然后替换 .
然后返回