自定义ClassLoader的实现

2019-12-05 21:22:36 浏览数 (1)

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 文件。

代码语言:javascript复制
public class HelloWorld{
  public HelloWorld(){
    System.out.println("创建了HelloWorld对象");
  }
}

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

代码语言:javascript复制
javac HelloWorld.java

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

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

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

代码语言:javascript复制
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 对象。

代码语言:javascript复制
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 的用法,你学会了吗?

0 人点赞