加载流程
ClassLoader 的整个加载思路在 ClassLoader#loadClass(String, boolean) 里面,采用模板方法设计模式:
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{// 判断当前要加载的类是否正在加载中synchronized (getClassLoadingLock(name)) {// 通过类名查找该Class是否已经加载过了Class<?> c = findLoadedClass(name);// 如果该类没有加载过if (c == null) {long t0 = System.nanoTime();try {// 让父加载器走一遍这个流程if (parent != null) {c = parent.loadClass(name, false);} else {// 如果没有parent了,即自己是Boot了,就自己尝试loadClass一下c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {}// 如果父加载器没有找到,那么当前这个类加载器去查找一下if (c == null) {long t1 = System.nanoTime();c = findClass(name);// 性能分析器?影响不大,略过sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}// 如果调用了resolve,那么直接开始处理加载这个类(如果c是null,会抛出ClassNotFound)if (resolve) {resolveClass(c);}return c;}}
然后再结合这张图来理解一下:
自定义ClassLoader
所以我们想要自定义ClassLoader,就只需要重新实现 findClass() 即可。这里打算自定义以下三种类加载器:
- 文件类加载器
- 网络类加载器
- 热部署类加载器
FileClassLoader
通过重新定义 findClass() ,将查找逻辑 findClass() 重新实现,因为 loadClass() 是模板方法,会去调用子类的 findClass() 来查找类:
public class FileClassLoader extends ClassLoader{@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 新路径String path = "/home/codeleven/Desktop/test/";// 替换com.codeleven.bean.SingletonBean -> com/codeleven/bean/SingletonBean.classString classNamePath = name.replaceAll("\\.", "/");classNamePath += ".class";String complete = path + classNamePath;try {// 获取到对应文件的字节码FileInputStream fileInputStream = new FileInputStream(complete);int len = -1;byte[] bytes = new byte[2048];len = fileInputStream.read(bytes);// defineClass获取到对应的Class返回return this.defineClass(name, bytes, 0, len);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}}
UrlClassLoader
对于网络类加载器来说,重新实现的逻辑和 FileClassLoader 一样,通过给的类名去加载获取对应的字节码即可。这里就不再赘述了~
HotSwapClassLoader
基础原理是不同的类加载器加载同名的类获取到的 Class 不一样,可以重复加载~有空补充
