自定义ClassLoader

ClassLoader在Java中有着非常重要的作用,主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责将Class里的二进制数据流装载进系统,然后交给Java虚拟机进行连接,初始化等操作。

在加载阶段,虚拟机需要完成以下三件事:

  1. 通过类的全限名获取此类的二进制字节流。
  2. 将这个字节流所代表的静态存储结构转换为方法区的运行时数据区
  3. 在内存中生成一个代表这个类的Class对象,作为方法区的这个类的各种数据访问入口。

那么如何自定义ClassLoader呢?

其实只需要重写findClass即可,findClass在找到Class对象后,调用defineClass来定义Class字节流。

代码

  1. 先新建一个Java文件,在系统上的位置,然后用javac编译java文件。
//编译
//javac src/test/java/me/aihe/demo/Hello.java 
public class Hello {
    static {
        System.out.println("hello");
    }
}
  1. 新建ClassLoader,重写findClass方法。
public class MyClassLoader extends ClassLoader {
    private String path;
    private String classLoaderName;

    public MyClassLoader(String path, String classLoaderName) {
        this.path = path;
        this.classLoaderName = classLoaderName;
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        try {

            byte[] b = loadClassData(name);
            return defineClass(name, b, 0, b.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private byte[] loadClassData(String name) throws IOException {
        name = path + name + ".class";
        InputStream is = null;
        ByteArrayOutputStream outputStream = null;
        try {
            is = new FileInputStream(new File(name));
            outputStream = new ByteArrayOutputStream();
            int i = 0;
            while ((i = is.read()) != -1) {
                outputStream.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
            if (is != null) {
                is.close();
            }
        }

        return outputStream.toByteArray();
    }
}
  1. 新建测试类,看看刚才写的ClassLoader是否生效。
public class ClassLoaderChecker {
    
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader myClassLoader = new MyClassLoader("/Users/aihe/Desktop/extra/spring-boot-learn/src/test/java/me/aihe/demo/", "random");
        Class c = myClassLoader.loadClass("Hello");
        System.out.println("ClassLoader:" + c.getClassLoader());
        Object instance = c.newInstance();
    }

}
  1. 运行查看结果,可以看到尽管Class文件是被我们自己的ClassLoader加载的,并且Class对象可以用来新建Java对象。


    image.png

最后

自定义类加载器有很多好处,它让Java代码更加灵活,应用场景:

  • 可以用来加密Class文件,然后解密Class文件进行再加载,提高系统安全性
  • 从网络中加载Class文件

参考

  • JVM是如何加载类的?

你可能感兴趣的:(自定义ClassLoader)