自定义类加载器实战

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

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

 1 package classloader;
 2 
 3 import java.io.ByteArrayOutputStream;
 4 import java.io.File;
 5 import java.io.FileInputStream;
 6 
 7 public class MyClassLoader extends ClassLoader {
 8         
 9     @Override
10     public Class<?> findClass(String name) {
11         FileInputStream in;
12         try {
13             in = new FileInputStream(
14                     new File("/Users/cz/java/" + name.replace( '.', '/') + ".class"));
15         
16             ByteArrayOutputStream out = new ByteArrayOutputStream();
17             int ch = 0;
18             while ((ch = in.read()) != -1) {
19                 out.write(ch);
20             }
21             byte[] data = out.toByteArray();
22             return defineClass(name, data, 0, data.length);
23         } catch (Exception e) {
24             e.printStackTrace();
25             return null;
26         }
27     }
28     
29     public static void main(String[] args) throws Exception {
30         MyClassLoader myClassLoader1 = new MyClassLoader();
31         Class<?> clazz1 = myClassLoader1.loadClass("CZ");
32         System.out.println(clazz1.getClassLoader());
33         
34         MyClassLoader myClassLoader2 = new MyClassLoader();
35         Class<?> clazz2 = myClassLoader2.loadClass("CZ");
36         System.out.println(clazz2.getClassLoader());
37         
38         // 验证isInstance方法是否会比较classloader
39         Object obj1 = clazz1.newInstance();
40         Object obj2 = clazz2.newInstance();
41         System.out.println(clazz1.isInstance(obj1));
42         System.out.println(clazz1.isInstance(obj2));
43     }
44 }

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

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

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

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

下面是运行结果:

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

 

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

如果你的父类加载器是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

 

posted @ 2017-05-16 14:21  大诚挚  阅读(449)  评论(0编辑  收藏  举报