­

自定义ClassLoader的实现

  • 2019 年 12 月 5 日
  • 筆記

ClassLoader 是 JVM 结构中很重要的一块,也可以说是整个 JVM 运行机制的入口,程序通过 ClassLoader 将编译好的字节码文件加载到内存中,生成 Class,进而创建对象,之后才能进行各种运算、解析,最终生成机器码提交到操作系统中。

JVM 结构如下图所示。

ClassLoader 有 4 种具体的分类:

1、BootstrapClassLoader

2、ExtClassLoader

3、AppClassLoader

4、自定义 ClassLoader

BootstrapClassLoader 用来加载 Java 的核心类库,即存放在 java.* 包中的字节码文件,如 java.util.List、java.io.InputStream、java.lang.Integer 等。

ExtClassLoader 用来加载 Java 的扩展类库,即存放在 javax.* 包中的字节码文件,如 javax.sql.DataSource、javax.net.SocketFactory 等。

AppClassLoader 用来加载当前程序所在目录的类,即开发者自己编写的 Java 文件对应的字节码文件。

自定义的 ClassLoader 指开发者根据具体需求编写的类加载器,可以实现定制化加载。

接下来我们来实现一个自定义的 ClassLoader,首先是准备工作,ClassLoader 的作用是将字节码文件加载到内存中,所有你得先有字节码文件。

1、创建一个 Java 文件。

public class HelloWorld{    public HelloWorld(){      System.out.println("创建了HelloWorld对象");    }  }

2、将其编译成字节码文件。

javac HelloWorld.java

有了字节码文件,接下来就可以编写自定义 ClassLoader,并通过其加载字节码文件了,ClassLoader 具体的工作流程主要是通过两个方法完成类加载的,分别是 findClass 和 defineClass。

findClass 根据路径加载字节码文件,然后交给 defineClass,再把字节码转化为 Class。自定义 ClassLoader 需要开发者对 findClass 方法进行重写,完成加载字节码文件的操作,之后再将字节数据传给 ClassLoader 的 defineClass 方法即可,根据这个思路,我们来实现代码。

3、创建自定义 ClassLoader,并继承 ClassLoader。

import java.io.*;    public class MyClassLoader extends ClassLoader {      private String path;        public MyClassLoader(String path) {          this.path = path;      }        @Override      protected Class<?> findClass(String name) throws ClassNotFoundException {          String classPath = path+name+".class";          InputStream inputStream = null;          ByteArrayOutputStream outputStream = null;          try {              inputStream = new FileInputStream(classPath);              outputStream = new ByteArrayOutputStream();              int temp = 0;              while((temp = inputStream.read()) != -1){                  outputStream.write(temp);              }          } catch (FileNotFoundException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }finally {              try {                  outputStream.close();                  inputStream.close();              } catch (IOException e) {                  e.printStackTrace();              }          }          byte[] bytes = outputStream.toByteArray();          return defineClass(name,bytes,0,bytes.length);      }  }

在 findClass 方法中,我们通过 IO 流读取本地编译好的字节码文件,生成字节数组,再将字节数组传给 ClassLoader 的 defineClass 方法即可。

4、进行测试,实例化 MyClassLoader,并通过该实例化对象加载字节码文件获取 Class 对象。

public class Test {      public static void main(String[] args) {          MyClassLoader myClassLoader = new MyClassLoader("/Users/southwind/myjava/");          try {              Class clazz = myClassLoader.findClass("HelloWorld");              System.out.println(clazz);              System.out.println(clazz.getConstructor().newInstance());          } catch (Exception e) {              e.printStackTrace();          }      }  }

5、运行结果如下图所示。

HelloWorld 字节码文件加载成功,以上就是自定义 ClassLoader 的用法,你学会了吗?