JVM-用户自定义类加载器代码解析

 
 
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 
 * @author ASUS
 *MyClassLoader.java
 *
 *         Sample is loaded bysun.misc.Launcher$AppClassLoader@73d16e93 Dog is
 *         loaded bysun.misc.Launcher$AppClassLoader@73d16e93 Sample is loaded
 *         byloader3 Dog is loaded byloader3
 */
public class MyClassLoader extends ClassLoader
{
	private String name;// 类加载器的名字

	private String path = "e:\\";// 加载类的路径

	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;

	}

	@Override
	public String toString()
	{
		return this.name;
	}

	public String getPath()
	{
		return path;
	}

	public void setPath(String path)
	{
		this.path = path;
	}

	// 自定义类加载器
	// 查找并加载类的二进制数据
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException
	{
		byte[] data = this.loadClassDate(name);// 获得class文件的二进制数组

		return this.defineClass(name, data, 0, data.length);// 将字节数组转换为class对象
	}

	// 获得class文件的二进制数组
	private byte[] loadClassDate(String name)
	{
		InputStream is = null;
		byte[] data = null;
		ByteArrayOutputStream baos = null;

		try
		{
			this.name = this.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 (Exception e)
		{
			// TODO: handle exception
			e.printStackTrace();
		} finally
		{
			try
			{
				is.close();
				baos.close();
			} catch (Exception e2)
			{
				// TODO: handle exception
				e2.printStackTrace();
			}
		}
		return data;// 将class文件的字节数组作为返回值

	}

	public static void main(String[] args) throws Exception
	{
		
		MyClassLoader loader1 = new MyClassLoader("loader1");// loader1的类加载器是系统类加载器

		loader1.setPath("e:\\myapp\\severlib\\");

		MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");// loader2的类加载器是loader1

		loader2.setPath("e:\\myapp\\clientlib\\");

		MyClassLoader loader3 = new MyClassLoader(null, "loader3");// loader3的类加载器是根加载器

		loader3.setPath("e:\\myapp\\otherlib\\");

		test(loader2);
		test(loader3);

	}

	public static void test(ClassLoader loader) throws Exception
	{
		// loadClass中调用findClass
		Class clazz = loader.loadClass("Sample");

		Object object = clazz.newInstance();

	}

}

public class Sample
{
	public int v1 = 1;

	public Sample()
	{
		System.out.println("Sample is loaded by" + this.getClass().getClassLoader());

		new Dog();
	}
}

public class Dog
{
	public Dog()
	{
		System.out.println("Dog is loaded by" + this.getClass().getClassLoader());
	}
}

.class文件存放:


如何运行:

在DOS下运行,因为如果在eclipse下运行,那系统类加载器默认的当前目录中总会包含Sample和Dog的.class文件,这样一来,因为loader1的父类加载器是系统类加载器,测试用loader1加载的时候,它总会使得系统类加载器可以成功加载。因此,将.class文件分别放在了otherlib和severlib中,主函数运行类放在了syslib中,这个文件夹也就称为了系统类加载器的的加载路径。


运行结果:



结果说明:

MyClassLoader   是程序含主函数的类,存放在E:\myapp\syslib路径下,此时默认的系统类加载器的classpath或称为加载路径即为当前目录

Sample 放在E:\myapp\severlib

Dog 放在E:\myapp\severlib

 Section1:

test(loader2);

loader2类加载器的组合结构

loader2->loader1->系统类加载器->扩展类加载器->根类加载器

由于扩展类和根类加载器均不能加载Sample类,所以系统类加载器检查是否能加载,因为当前目录也就是系统类加载器的加载目录中不含有Sample,所以由loader1加载,因为loader1中有Sample,所以成功加载,此时loader1即为Sample的定义类加载器,并将SampleClass对象的引用返回给loader2;Dog同理。

test(loader3);

loader2类加载器的组合结构

Loader3->根类加载器

根类加载器无法加载Sample,所以loader3自行加载,其加载路径中包含Sample所以成功加载


 Section2:

此处设置classpath不仅是当前目录E:\myapp\syslib,而且添加了新目录E:\myapp\severlib,因为新路径下包含Sample,所以当一层一层委托父加载器加载时,到系统类加器的时候,可以成功加载SampleDog同理;

 Section3:

此处先把Dog.class文件放到了当前目录,所以在系统类加载Dog时可以成功加载;而Sample仍有loader1加载

Section4:

此处不仅把Dog.class文件放入了当前目录,还添加了新路径到classpath中,新路径包含SampleDog,所以系统类加载器成功加载。


如需补充,待续。。。


猜你喜欢

转载自blog.csdn.net/u011296723/article/details/80277504