自定义类加载器实战
本次尝试在不突破双亲委派模型的基础上,自定义类加载器,以理解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