Classloader中文叫做类加载器,作用就是把编译好的class文件加载到jvm中。但是,在jvm启动的时候,并不是一次性全部加载的。
AppClassLoader的父加载器(不是父类
)是Extention ClassLoader,Extention ClassLoader的父加载器(不是父类
)是Bootstrap ClassLoader。
我们可以写一些代码来验证一下。
public static void main(String[] args) {
System.out.println(User.class.getClassLoader());
System.out.println(RequestDTO.class.getClassLoader().getParent());
System.out.println(RequestDTO.class.getClassLoader().getParent().getParent());
}
其中User类是我们自己创建的一个类,运行结果如下:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@614ddd49
null
前面两个结果是预期之内,最后一个获取Extention ClassLoader的结果为null是为什么呢?
其实,Bootstrap ClassLoader是由C++编写的,它本身就是虚拟机的一部分,所以他并不是一个Java类,也就是无法在java代码中获取它的引用。
指的是类加载机制。
AppClassLoader加载class的时候,它首先会去缓存里面查这个类是不是已经加载过了
,如果没有,则让自己的父加载器去加载
,一直递归,直到Bootstrap,如果Bootstrap ClassLoader也判断这个类没有被加载过,则会去jre lib
下面去加载。如果还没加载到,则让自己子类Extention ClassLoader去jre lib ext
下加载,如果它也加载不到,则让它的子类AppClassLoader去classpath
下加载。总结一句话就是,由下向上委托,由上向下查找
。
为什么是这样子可以从代码中体现。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
//对应上面的先去缓存里面查找,底层是C的方法
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//对应上面的让父加载器去查找,递归
c = parent.loadClass(name, false);
} else {
//说明父加载器是Bootstrap ClassLoader,底层也是C的方法
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();
//由于是递归,对应上面的从上到下查找
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;
}
}
package com.curtain.classloader;
import java.io.*;
/**
* @Author Curtain
* @Date 2022/11/1 16:11
* @Description
*/
public class DiskClassLoader extends ClassLoader {
private String mLibPath;
public DiskClassLoader(String mLibPath) {
this.mLibPath = mLibPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String fileName = getFileName(name);
File file = new File(mLibPath, fileName);
try{
//通过流将class文件读取进来,通过defineClass方法加载class类
FileInputStream is = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len = 0;
while ((len = is.read()) != -1){
bos.write(len);
}
byte[] bytes = bos.toByteArray();
bos.close();
is.close();
return defineClass(name, bytes, 0, bytes.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
private String getFileName(String name) {
int index = name.lastIndexOf(".");
if (index == -1) {
return name + ".class";
} else {
return name.substring(index + 1) + ".class";
}
}
}
再写一个Test类,待会儿我们就用加载这个类。
package com.curtain.classloader;
/**
* @Author Curtain
* @Date 2022/11/1 14:42
* @Description
*/
public class Test {
public void say(){
System.out.println("Say Hello");
}
}
我们编译好Test类,将 class文件放在一个地方,比如,D盘下的lib文件夹。
最后就是测试类,DiskClassLoaderTest
package com.curtain.classloader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Author Curtain
* @Date 2022/11/1 16:19
* @Description
*/
public class DiskClassLoaderTest {
public static void main(String[] args) {
//创建自定义ClassLoader对象
DiskClassLoader diskClassLoader = new DiskClassLoader("D:\\lib");
try{
//加载class文件
Class c = diskClassLoader.loadClass("com.curtain.classloader.Test");
if (c != null){
try{
Object o = c.newInstance();
Method method = c.getDeclaredMethod("say", null);
method.invoke(o, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果就是成功执行了Test类里面的say()方法