类加载器加载顺序
- Bootstrap ClassLoader
- ExtClassLoader
- AppClassLoader
为啥加载顺序是这样的?从Launcher类说起
Launcher类
分析Launcher构造器源码
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//AppClassLoader 父加载器 ExtClassLoader
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
.......
}
Launcher类中封装了ExtClassLoader和AppClassLoader
分析ExtClassLoader和AppClassLoader源码
static class ExtClassLoader extends URLClassLoader {
public ExtClassLoader(File[] var1) throws IOException {
// ExtClassLoader 父加载器 null
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
......
}
}
static class AppClassLoader extends URLClassLoader {
.......
}
从以上源码可知:
1)AppClassLoader的父加载器是ExtClassLoader
2)ExtClassLoader的父加载器是null
3)Thread.currentThread().getContextClassLoader()获取的是AppClassLoader
那为啥说ExtClassLoader父加载器是Bootstrap ClassLoader?分析ClassLoader源码
ClassLoader类
分析构造函数
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
......
}
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
根据构造函数可知:
1)每个类加载器都有一个父加载器
2)如果ClassLoader创建时没有指定parent,那它的parent默认从getSystemClassLoader()获取,就是AppClassLoader
分析loadClass方法源码
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先, 检查类是否已经被加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//如果父加载器不为空
c = parent.loadClass(name, false);
} else {
//如果父加载器为空,使用Bootstrap ClassLoader加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// 如果还是没找到类,那就使用findclass方法查找类
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
分析loadClass方法源码可知:
当使用AppClassLoader加载类的时候,先递归调用AppClassLoader的父加载器ExtClassLoader加载类,然后递归调用ExtClassLoader的父加载器加载类,因为ExtClassLoader的父加载器是null,最终会使用Bootstrap ClassLoader加载类(Bootstrap ClassLoader是c++方法编写),所以我们可以认为ExtClassLoader的父加载器是Bootstrap ClassLoader
加载器加载类的路径
- Bootstrap ClassLoader :System.getProperty("sun.boot.class.path")
- ExtClassLoader :System.getProperty("java.ext.dirs")
- AppClassLoader :System.getProperty("java.class.path")
查看sun.misc.Launcher.AppClassLoader源码
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
.......
}
查看sun.misc.Launcher.ExtClassLoader源码
private static File[] getExtDirs() {
String var0 = System.getProperty("java.ext.dirs");
......
}
查看Launcher源码
private static String bootClassPath = System.getProperty("sun.boot.class.path");
测试例子
public class ClassLoadTest {
public static void main(String[] args) {
ClassLoader cl = ClassLoadTest.class.getClassLoader();
System.out.println("ClassLoader is:"+cl.toString());
System.out.println("ClassLoader is:"+Thread.currentThread().getContextClassLoader());
System.out.println("ClassLoader is:"+cl.getParent().toString());
System.out.println("ClassLoader is:"+ClassLoader.getSystemClassLoader().toString());
System.out.println("ClassLoader is:"+Launcher.getLauncher().getClassLoader().toString());
System.out.println("ClassLoader is:"+Launcher.getLauncher().getClassLoader().getParent().toString());
// String 和 int 的 classload 都是 null , 是由Bootstrap ClassLoader 加载
//ystem.out.println(int.class.getClassLoader().toString());
//System.out.println(String.class.getClassLoader().toString());
}
}
测试结果
ClassLoader is:sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader is:sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader is:sun.misc.Launcher$ExtClassLoader@6ff3c5b5
ClassLoader is:sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader is:sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader is:sun.misc.Launcher$ExtClassLoader@6ff3c5b5