一、类加载器简介
java中自带的类加载器可以分为根类加载器(BootStrap classloader),扩展类加载器,应用类加载器,这三个都不是用java语言实现的。
其中根类加载器和扩展类加载器用来加载java自带的一些类,而应用类加载器用来加载我们自己写的java类编译后的class文件,也就是classpath中的class文件。
类加载器在java中的继承体系是这样的,顶层父类是ClassLoader
注意扩展类加载器和APPClassloader实际不是用java语言实现的。自定义类加载器时可以继承UrlClassLoader
类加载器加载资源的时候采用的是双亲委派机制:1.每个类加载器对它加载过的类都会缓存,
2.向上委托查找,向下委托加载。只有它的父亲中找不到某个类时才会自己去加载某个类
所以加载某个类时是按这样的顺序去加载
要注意类加载之间的父子关系不是通过继承实现的,是通过持有parent属性实现的
BootStrap classloader ==> ExtClassLoader ==> AppClassLoader,
AppClassLoader中持有的parent属性是ExtClassLoader ,
ExtClassLoader中持有的parent属性是BootStrap classloader,也就是根类加载器
BootStrap classloader中持有的parent属性是null
二. ClassLoader中方法介绍
ClassLoader是类加载器的顶层抽象类,其中定义了类加载的一些通用方法
AppClassLoader,ExtClassLoader,还有自定义类加载器都会继承它
2.1 loadClass(String name, boolean resolve)
这方法是双亲委派机制的实现,
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
//某个类加载器从自己的缓存中看是否加载过某个类
Class<?> c = findLoadedClass(name);
//没加载过,向上委托父类查找
if (c == null) {
long t0 = System.nanoTime();
try {
//这一块是双亲委派,让父加载器查找的实现
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//让根类加载器去查找类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
//走到这里表示在父加载器中没找到,需要让子加载器加载
long t1 = System.nanoTime();
//调用自身的findClass方法去加载类
c = findClass(name);
// this is the defining class loader; record the stats
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
2.2 findClass(String name)
这个方法在ClassLoader中直接抛出异常,需要让子类去重写它实现自己的加载逻辑
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
一个具体类加载器的findClass方法一般会先加载类的文件得到字节数组,然后调用defineClass方法转成Class对象返回,类似下面这样,这个defineClass是ClassLoader中的方法
public Class findClass(String name) {
//读取文件得到字节数组
byte[] b = loadClassData(name);
//把字节数组转成Class文件,该方法定义在ClassLoader中
return defineClass(name, b, 0, b.length);
}
2.3 defineClass
defineClass方法用来把自己数组转成Class对象,提供给findClass方法调用
三、自定义类加载
所以我们自定义类加载时,一般会继承ClassLoader,然后重写其findClass方法,这样不会破坏双亲委派机制,如果需要打破双亲委派机制则需要重写loadClass方法。