jdk1.8 中 AppClassLoader 与 ExtClassLoader 都继承于 URLClassLoader。
AppClassLoader 与 ExtClassLoader 没有重写 findClass 方法,URLClassLoader重写了 findClass 方法。
故 findClass 都指向 URLClassLoader。
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
Resource res = ucp.getResource(path, false);
private final URLClassPath ucp;
public Resource getResource(String name, boolean check) {
if (DEBUG) {
System.err.println("URLClassPath.getResource(\"" + name + "\")");
}
Loader loader;
for (int i = 0; (loader = getLoader(i)) != null; i++) {
Resource res = loader.getResource(name, check);
if (res != null) {
return res;
}
}
return null;
}
private static class Loader implements Closeable {
private final URL base;
private JarFile jarfile;
Loader(URL var1) { this.base = var1; }
URL getBaseURL() { return this.base; }
// 子类实现
URL findResource(String var1, boolean var2) {}
Resource getResource(final String var1, boolean var2) {}
// .........
Resource getResource(String var1) { return this.getResource(var1, true); }
public void close() throws IOException {
if (this.jarfile != null) {
this.jarfile.close();
}
}
URL[] getClassPath() throws IOException {
return null;
}
}
Loader 的实现有两个:
private static class FileLoader extends URLClassPath.Loader {}
static class JarLoader extends URLClassPath.Loader {}
sun.misc.URLClassPath.JarLoader#getResource(java.lang.String, boolean)
@Override
Resource getResource(final String name, boolean check) {
//
if (metaIndex != null) {
if (!metaIndex.mayContain(name)) {
return null;
}
}
try {
// 确保 jar 存在
ensureOpen();
} catch (IOException e) {
throw new InternalError(e);
}
final JarEntry entry = jar.getJarEntry(name);
if (entry != null) {
return checkResource(name, check, entry);
}
if (index == null) {
return null;
}
HashSet<String> visited = new HashSet<String>();
return getResource(name, check, visited);
}
metaIndex 需要调用 sun.misc.MetaIndex#registerDirectory 注册。
JVM 默认注册的如下:
JarFile JarEntry的使用:
@Test
public void test1() {
JarFile jar = null;
InputStream inputStream = null;
try {
jar = new JarFile("/Users/N3verL4nd/.m2/repository/com/sankuai/mms/mms-boot-jetty9/1.3.5/mms-boot-jetty9-1.3.5.jar");
JarEntry entry = jar.getJarEntry("jetty9.xml");
inputStream = jar.getInputStream(entry);
StreamUtils.copy(inputStream, System.out);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (jar != null) {
try {
jar.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
sun.misc.URLClassPath.FileLoader#getResource 就是文件夹和文件的拼接。
可以看到查找都是相对路径。