📚《深入浅出JVM字节码》

《Java虚拟机字节码:从入门到实战》的开源版本。作者通过自己的实战经验,整合出一套适合新手的高效学习教程。归纳并提炼知识点,制定合理路线,帮助读者更快掌握核心技术。


自定义类加载器

Java还提供了一种支持通过网络下载方式加载class的类加载器URLClassLoader,也支持传本地路径,但不能直接传入字节数组加载。而其它类加载器只能通过类名去加载。

由于动态编写字节码生成class或者改写class是在内存中完成的,如果要使用Java提供的几种类加载器去加载,我们需要先将字节码输出到文件并将文件存放在classpath的目录下,因此我们可以自己实现一个类加载器支持直接传入字节数组加载,省略掉这一步骤。

ClassLoader 类有三个重要的方法,分别是 loadClass、findClass 和 defineClass。loadClass方法是加载目标类的入口,首先查找当前ClassLoader以及父加载器是否已经加载了目标类,如果都没有加载,且父加载器加载不到这个类,就会调用 findClass 让自己来加载目标类。ClassLoader 的 findClass方法是需要子类重写的,在该方法中实现获取目标类的字节码,拿到类的字节码之后再调用 defineClass方法将字节码转换成Class对象。

自定义类加载器需要继承ClassLoader,并重写findClass方法,在findClass方法中根据类名取得该类的字节码,再调用父类的defineClass方法完成类的加载。需要注意的是,findClass方法传递进行的类名是以符号“.”拼接的类名,不是“/”。使用“/”符号替代“ .”符号的类名称为类的内部名称或内部类名。

自定义类加载器ByteCodeClassLoader的实现如下代码所示。

public class ByteCodeClassLoader extends ClassLoader {

    // 类名-> 字节码持有者
    private final Map<String, ByteCodeHolder> classes = new HashMap<>();

    public ByteCodeClassLoader(final ClassLoader parentClassLoader) {
        super(parentClassLoader);
    }

    @Override
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
        ByteCodeHolder holder= classes.get(name);
        if (holder != null) {
            byte[] bytes = holder.getByteCode();
            classes.remove(name);
            return defineClass(name, bytes, 0, bytes.length);
        }
        return super.findClass(name);
    }

    public void add(final String name, final ByteCodeHolder holder) {
        classes.put(name, holder);
    }
}

ByteCodeClassLoader使用HashMap缓存类名与其字节码的映射,外部需要先通过调用add方法将需要加载的类添加到Map中,才能调用loadClass方法加载类。在findClass方法中,完成类的加载后将调用add方法添加的数据移除。


发布于:2021 年 07 月 03 日
作者: 吴就业
链接: https://github.com/wujiuye/JVMByteCodeGitBook
来源: Github Pages 开源电子书《深入浅出JVM字节码》(《Java虚拟机字节码从入门到实战》的第二版),未经作者许可,禁止转载!


📚目录