加载流程

ClassLoader 的整个加载思路在 ClassLoader#loadClass(String, boolean) 里面,采用模板方法设计模式

  1. protected Class<?> loadClass(String name, boolean resolve)
  2. throws ClassNotFoundException
  3. {
  4. // 判断当前要加载的类是否正在加载中
  5. synchronized (getClassLoadingLock(name)) {
  6. // 通过类名查找该Class是否已经加载过了
  7. Class<?> c = findLoadedClass(name);
  8. // 如果该类没有加载过
  9. if (c == null) {
  10. long t0 = System.nanoTime();
  11. try {
  12. // 让父加载器走一遍这个流程
  13. if (parent != null) {
  14. c = parent.loadClass(name, false);
  15. } else {
  16. // 如果没有parent了,即自己是Boot了,就自己尝试loadClass一下
  17. c = findBootstrapClassOrNull(name);
  18. }
  19. } catch (ClassNotFoundException e) {
  20. }
  21. // 如果父加载器没有找到,那么当前这个类加载器去查找一下
  22. if (c == null) {
  23. long t1 = System.nanoTime();
  24. c = findClass(name);
  25. // 性能分析器?影响不大,略过
  26. sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
  27. sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
  28. sun.misc.PerfCounter.getFindClasses().increment();
  29. }
  30. }
  31. // 如果调用了resolve,那么直接开始处理加载这个类(如果c是null,会抛出ClassNotFound)
  32. if (resolve) {
  33. resolveClass(c);
  34. }
  35. return c;
  36. }
  37. }

然后再结合这张图来理解一下:
image.png

自定义ClassLoader

所以我们想要自定义ClassLoader,就只需要重新实现 findClass() 即可。这里打算自定义以下三种类加载器:

  1. 文件类加载器
  2. 网络类加载器
  3. 热部署类加载器

FileClassLoader

通过重新定义 findClass() ,将查找逻辑 findClass() 重新实现,因为 loadClass() 是模板方法,会去调用子类的 findClass() 来查找类:

  1. public class FileClassLoader extends ClassLoader{
  2. @Override
  3. protected Class<?> findClass(String name) throws ClassNotFoundException {
  4. // 新路径
  5. String path = "/home/codeleven/Desktop/test/";
  6. // 替换com.codeleven.bean.SingletonBean -> com/codeleven/bean/SingletonBean.class
  7. String classNamePath = name.replaceAll("\\.", "/");
  8. classNamePath += ".class";
  9. String complete = path + classNamePath;
  10. try {
  11. // 获取到对应文件的字节码
  12. FileInputStream fileInputStream = new FileInputStream(complete);
  13. int len = -1;
  14. byte[] bytes = new byte[2048];
  15. len = fileInputStream.read(bytes);
  16. // defineClass获取到对应的Class返回
  17. return this.defineClass(name, bytes, 0, len);
  18. } catch (FileNotFoundException e) {
  19. e.printStackTrace();
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. return null;
  24. }
  25. }

UrlClassLoader

对于网络类加载器来说,重新实现的逻辑和 FileClassLoader 一样,通过给的类名去加载获取对应的字节码即可。这里就不再赘述了~

HotSwapClassLoader

基础原理是不同的类加载器加载同名的类获取到的 Class 不一样,可以重复加载~有空补充