JVM自定义类加载器实战

本次尝试在不突破双亲委派模型的基础上,自定义类加载器,以理解Java类加载的流程。

既然不突破双亲委派,那就只重写findClass方法:

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

public class MyClassLoader extends ClassLoader {
       
    @Override
    public Class<?> findClass(String name) {
        FileInputStream in;
        try {
            in = new FileInputStream(
                    new File("/Users/cz/java/" + name.replace( '.', '/') + ".class"));
       
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int ch = 0;
            while ((ch = in.read()) != -1) {
                out.write(ch);
            }
            byte[] data = out.toByteArray();
            return defineClass(name, data, 0, data.length);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
   
    public static void main(String[] args) throws Exception {
        MyClassLoader myClassLoader1 = new MyClassLoader();
        Class<?> clazz1 = myClassLoader1.loadClass("CZ");
        System.out.println(clazz1.getClassLoader());
       
        MyClassLoader myClassLoader2 = new MyClassLoader();
        Class<?> clazz2 = myClassLoader2.loadClass("CZ");
        System.out.println(clazz2.getClassLoader());
       
        // 验证isInstance方法是否会比较classloader
        Object obj1 = clazz1.newInstance();
        Object obj2 = clazz2.newInstance();
        System.out.println(clazz1.isInstance(obj1));
        System.out.println(clazz1.isInstance(obj2));
    }
}

在不指定父类加载器的情况下,类ClassLoader默认的构造方法里将父类加载器设置为了系统类加载器(AppClassLoader)

protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

所以我在findClass方法里面指定了路径,不在classpath里面,所以不会被AppClassLoader加载。

另外,我还验证了一下Class类的isInstance方法。同样的一份class文件,被两个类加载器加载后形成了两个类型。

下面是运行结果:

classloader.MyClassLoader@7852e922
classloader.MyClassLoader@70dea4e
true
false

下面再试试自定义类加载器去加载classpath里面的class文件

JVM自定义类加载器实战

如果你的父类加载器是AppClassLoader的话,根据双亲委派模型,是轮不到你的。所以我这里必须指定父类加载器,而且优先级必须要高于AppClassLoader。

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

public class MyClassLoader2 extends ClassLoader {

public MyClassLoader2() {
       
    }
   
    public MyClassLoader2(ClassLoader parent) {
        super(parent);
    }

@Override
    public Class<?> findClass(String name) {
        FileInputStream in;
        try {
            in = new FileInputStream(
                    new File("/Users/cz/myapp/workspace/JVM/bin/" +
                            name.replace( '.', '/') + ".class"));
       
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int ch = 0;
            while ((ch = in.read()) != -1) {
                out.write(ch);
            }
            byte[] data = out.toByteArray();
            return defineClass(name, data, 0, data.length);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
   
    public static void main(String[] args) throws Exception {
        // null代表启动类加载器
        MyClassLoader2 classLoader1 = new MyClassLoader2(null);
        Class<?> clazz1 = classLoader1.loadClass("classloader.MyClassLoader2");
        System.out.println(clazz1.getClassLoader());
       
        MyClassLoader2 classLoader2 = new MyClassLoader2();
        Class<?> clazz2 = classLoader2.loadClass("classloader.MyClassLoader2");
        System.out.println(clazz2.getClassLoader());
       
        Object obj = clazz2.newInstance();
        System.out.println(clazz1.isInstance(obj));
    }
}

总共生成了2个类加载器,classLoader1的父类加载器是启动类加载器,classLoader2的父类加载器是系统类加载器(达不到我们的要求)。

运行结果:

classloader.MyClassLoader2@7852e922
sun.misc.Launcher$AppClassLoader@2a139a55
false

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/948077fbf434e6402759bb37fd496249.html