10、自定义类加载器
自定义类加载器的3个步骤:
1、继承ClassLoader;
2、重写findClass()方法;
3、在findClass()方法调用defineClass()方法;
package com.shtec.classLoader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * 自定义类加载器的3个步骤: * 1、继承ClassLoader * 2、重写findClass()方法 * 3、在findClass()方法调用defineClass()方法 * * @author sunhao * */ public class CustomClassLoader extends ClassLoader{ private String classLoaderName; private String classPath; //类加载器的加载路径 private static final String suffix = ".class"; public CustomClassLoader(){} public CustomClassLoader(String classLoaderName){ super();//将系统类加载器当做该类加载器的父加载器 this.classLoaderName = classLoaderName; } public CustomClassLoader(ClassLoader parent, String classLoaderName){ super(parent);//显示指定该类加载器的父加载器 this.classLoaderName = classLoaderName; } public void setClassPath(String classPath) { this.classPath = classPath; } /* * *********************************************************************** * ClassLoader中的loadClass(String name, boolean resolve)方法会调用findClass方法* * 该方法执行,说明自定义类加载器生效 * * *********************************************************************** */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { System.out.println("自定义类加载器【" + this.classLoaderName + "】查找类" + name); byte[] data = this.loadClassData(name); //defineClass方法将字节数组转化为Class对象 return this.defineClass(name, data, 0, data.length); } /* * 加载.class文件,并转换为字节数组 */ private byte[] loadClassData(String className){ InputStream in = null; byte[] data = null; ByteArrayOutputStream baos = null; className = className.replace(".", "/"); try { in = new FileInputStream(new File(this.classPath + className + suffix)); baos = new ByteArrayOutputStream(); byte[] buffer = new byte[8192]; int len = 0; while((len=in.read(buffer)) != -1){ baos.write(buffer, 0, len); } data = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); }finally{ if(in != null){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if(baos != null){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } return data; } public static void main(String[] args) throws Exception { CustomClassLoader customClassLoader = new CustomClassLoader("customClassLoader"); customClassLoader.setClassPath("D:\\classes\\"); Class<?> clazz = customClassLoader.loadClass("com.shtec.classLoader.Test"); System.out.println("当前类加载器:" + clazz.getClassLoader());
//1、默认情况下,重写的“findClass()”方法不会执行,说明自定义的类加载器加载类的操作并没有生效
//输出:
//当前类加载器:sun.misc.Launcher$AppClassLoader@73d16e93
/*
* 原因:java类加载器对类的加载,遵循【双亲委派机制】,也就是说通过自定义的类加载器去加载"com.shtec.classLoader.Test"类时,
* 自定义类加载器会把加载任务先交给它的父加载器,而自定义类加载器的父加载器默认是【系统类加载器】(查看自定义类加载器的构造方法),
* 最终,系统类加载器发现(System.getProperties("java.class.path"))类路径下有对应Test类的.class文件,于是将其加载。
* 所以,重写的“findClass()”方法不会执行,最终打印出:【类加载器:sun.misc.Launcher$AppClassLoader@73d16e93】。
*/
/*
* 2、如果想让重写的“findClass()”方法执行,只需要将类路径下的Test.class文件删除,由于删除后,系统类加载器加载不了这个.class文件,因此,对“com.shtec.classLoader.Test.class”的加载工作将交由我们自定义的类加载器。
* 最终输出:
* 自定义类加载器【customClassLoader】查找类com.shtec.classLoader.Test
* 当前类加载器:com.shtec.classLoader.CustomClassLoader@15db9742
*/
}
}