深入JVM之自定义类加载器

完整版见 https://jadyer.github.io/




下面演示的是自定义的类加载器
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()方法自动调用
	 */
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] data = this.loadClassData(name);
		return this.defineClass(name, data, 0, data.length); //将字节数组转换为一个class类的实例
	}
	
	@Override
	public 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 Name
		MyClassLoader 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());
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值