深入JVM之自定义类加载器

来源:互联网 发布:linux 当前目录复制 编辑:程序博客网 时间:2024/05/21 16:11
下面演示的是自定义的类加载器
package com.jadyer.classloader;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;/** * 深入JVM之自定义类加载器 * @author 宏宇 * @editor Jan 25, 2012 6:14:44 PM */public class MyClassLoader extends ClassLoader {private String name;                      //类加载器的名字private String path = "D:\\";             //加载类的路径private final String fileType = ".class"; //class文件的扩展名public MyClassLoader(String name){super(); //让系统类加载器成为该类加载器的父加载器this.name = name;}public MyClassLoader(ClassLoader parent, String name){super(parent); //显式指定该类加载器的父加载器this.name = name;}/** * 根据类名,得到class文件的二进制的字节数组 * @author 宏宇 * @editor Jan 25, 2012 6:22:39 PM * @see 返回:因为类里面的数据本身是二进制代码,所以该方法的返回值必须定义成byte[]的形式 * @see 思路:从硬盘上通过输入流把二进制数据加载到内存,然后把它输出到一个字节数组输出流中 * @see 思路:然后在字节数组输出流里面将其转换为一个字节数组,赋给data,最后返回 */private byte[] loadClassData(String name){InputStream is = null;byte[] data = null; //最终所返回的字节数组ByteArrayOutputStream baos = null;try{name = name.replace(".", "\\"); //把点替换成斜杠is = new FileInputStream(new File(path + name + fileType));baos = new ByteArrayOutputStream();int ch = 0;while(-1 != (ch=is.read())){baos.write(ch);}data = baos.toByteArray(); //转换为字节数组}catch(FileNotFoundException e){e.printStackTrace();}catch(IOException e){e.printStackTrace();}finally{try{is.close();baos.close();}catch(IOException e){e.printStackTrace();}}return data;}/** * 该方法会被我们自定义的类加载器MyClassLoader.loadClass()方法自动调用 */@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {byte[] data = this.loadClassData(name);return this.defineClass(name, data, 0, data.length); //将字节数组转换为一个class类的实例}@Overridepublic String toString() {return this.name;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public static void main(String[] args) throws Exception {MyClassLoader loader11 = new MyClassLoader("loader11"); //这表示loader11的父加载器是系统类加载器loader11.setPath("D:\\Develop\\MyWorkspace\\InsideJVM\\bin\\"); //insideJVM is my Java Project NameMyClassLoader loader22 = new MyClassLoader(loader11, "loader22"); //将loader11作为loader22的父加载器loader22.setPath("D:\\Develop\\MyWorkspace\\InsideJVM\\src\\");MyClassLoader loader33 = new MyClassLoader(null, "loader33"); //null表示loader33的父加载器是根类加载器loader33.setPath("D:\\Develop\\MyWorkspace\\InsideJVM\\bin\\");/** * 如果MyAnimal类在load22的命名空间中还没有被加载,load22首先委托它的父加载器load11代为加载 * load11再请求系统类加载器代为加载,系统类加载器再请求扩展类加载器代为加载,扩展类加载器再请求根类加载器代为加载 * 根类加载器会从系统属性sun.boot.class.path所指定的目录中加载类库 * 扩展类加载器会从系统属性java.ext.dirs所指定的目录,以及,JDK安装目录的jre//lib//ext子目录(扩展目录)中加载类库 * 系统类加载器会从系统属性java.class.path或者环境变量classpath所指定的目录中加载类 * 结果根、扩展类加载器均不能加载,则系统类加载器尝试加载,于是加载成功,便将MyAnimal类所对应的Class对象的引用返回 * 补充:MyAnimal中主动使用了MyDog类,当执行MyAnimal类的构造方法中的new MyDog()语句时 *      JVM会使用MyAnimal类的定义类加载器去加载MyDog类,加载过程同样采用父亲委托机制 * 注意:当系统类加载器尝试加载该类时,它会在classpath路径下查找MyAnimal类,找到则尝试加载,找不到则交由load11去加载 *      所以,我们可以移动一下MyAnimal和MyPig的class文件位置,再运行"java MyClassLoader"命令,查看控制台打印结果 */loader22.loadClass("com.jadyer.classloader.MyAnimal").newInstance();System.out.println("=================================================================");/** * 同理,此处首先会发现MyAnimal在load33的命名空间中还没有被加载,load33首先委托它的父加载器根类加载器代为加载 * 结果根类加载器无法加载该类,于是load33尝试加载,加载成功,于是返回了MyAnimal类所对应的Class对象的引用 */loader33.loadClass("com.jadyer.classloader.MyAnimal").newInstance();System.out.println("=================================================================");}}

下面是自定义类加载器测试时用到的MyAnimal.java
package com.jadyer.classloader;/** * MyAnimal * @author 宏宇 * @editor Jan 25, 2012 8:46:28 PM */public class MyAnimal {public int v1 = 1;public MyAnimal(){System.out.println("MyAnimal is loaded by: " + this.getClass().getClassLoader());new MyPig(); //主动使用Pig类}}

下面是自定义类加载器测试时用到的MyPig.java
package com.jadyer.classloader;/** * MyPig * @author 宏宇 * @editor Jan 25, 2012 8:46:34 PM */public class MyPig {public MyPig(){System.out.println("   MyPig is loaded by: " + this.getClass().getClassLoader());}}
原创粉丝点击