Java类加载器 简单自定义类加载器

本文深入探讨自定义类加载器的实现流程,包括继承java.lang.ClassLoader,委派加载请求给父类加载器,以及定义自定义加载方法。通过具体代码示例,介绍了文件系统类加载器和网络类加载器的创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自定义类加载器的流程:

  • 继承java.lang.ClassLoader
  • 首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载,直接返回
  • 委派类加载器请求给父类加载器,如果父类加载器能够完成 则返回父类加载器加载的Class实例
  • 调用本类加载器的findClass(…)方法,试图获取相应的自己码,如果获取得到,则调用defineClass(…)方法导入类型到方法区;如果获取不到字节码或者其他原因失败,返回异常给loadClass(…),loadClass(…)转抛异常,终止加载过程
  • 注意,被两个类加载器加载的同一个类,JVM不认为是相同的类

注2:
loadClassAPI

自定义一个加载字节码文件(.class)的类加载器

package com.feng.ClassTest;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * 自定义文件系统类加载器
 * @author feng
 *
 */

// 传入指定的目录,目录下class文件可以正常加载
// com.feng.ClassTest.User --> D:/myjava/ com/feng/ClassTest/User.class
public class FileSystemClassLoader extends ClassLoader {
	
	private String rootDir;
	
	public FileSystemClassLoader(String rootDir) {
		this.rootDir = rootDir;
	}
	
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		
		Class<?> c = null;
		
		//	-----------------------
		c = findLoadedClass(name);	//是否能在已加载类中找到
		if(c != null) {
			return c;						//如果已加载,直接返回
		}
		
		ClassLoader parent = this.getParent();	//获得父类加载器,就是应用程序类加载器AppClassLoader
		try {
			c = parent.loadClass(name);	//父类加载器加载该类,父类加载该类过程就使用了双亲委托机制
		} catch (Exception e) {
			// e.printStackTrace();
		}

		if(c != null) {						// 如果父类加载器成功加载该类 
			return c;							// 返回父类加载器加载的类
		}
		//		-----------------------
		
		byte[] classData = getClassData(name);	// 自定义方法获取类字节码
		if(classData == null) {
			throw new ClassNotFoundException();	// 如果字节数组为空,抛出异常
		} else {
			c = defineClass(name, classData, 0, classData.length);
		}
		
		return c;
		
	}
	
	private byte[] getClassData(String classname) {
		// 拼出类class文件地址
		String path = rootDir + "/" + classname.replace('.', '/') + ".class";
		
		InputStream is = null;
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			is = new FileInputStream(path);
			
			byte[] buffer = new byte[1024];
			int temp = 0;
			
			while((temp = is.read(buffer)) != -1) {
				baos.write(buffer, 0, temp);
			}
			
			return baos.toByteArray();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			return null;
		} finally {
			
			try {
				if(is != null) {
				is.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			try {
				if(baos != null) {
					baos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		
	}
	
}

测试该类

package com.feng.ClassTest;

/**
 * 测试自定义FileSystemClassLoader
 * @author Administrator
 *
 */

public class Demo03 {

	public static void main(String[] args) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		
		FileSystemClassLoader loader = new FileSystemClassLoader("E:\\myjava");
		FileSystemClassLoader loader2 = new FileSystemClassLoader("E:\\myjava");
		
		Class<?> c1 = loader.findClass("com.feng.Hello.Hello");
		Class<?> c2 = loader.loadClass("com.feng.Hello.Hello");
		Class<?> c3 = loader2.loadClass("com.feng.Hello.Hello");
		
		Class<?> c4 = loader2.findClass("com.feng.ClassTest.Demo01");
		Class<?> c5 = loader2.findClass("java.lang.String");

		
		System.out.println(c1);					// class com.feng.Hello.Hello
		
		// 	同一个类被不同加载器加载,JVM也认为是不同的类。只有同一个类加载器加载的同一个类JVM才认为是一类
		System.out.println(c1.hashCode());		//1550089733
		System.out.println(c2.hashCode());		// 1550089733
		System.out.println(c3.hashCode());		// 865113938

		
		System.out.println(c3.getClassLoader());	//com.feng.ClassTest.FileSystemClassLoader@4e25154f
		System.out.println(c4.getClassLoader());	//sun.misc.Launcher$AppClassLoader@73d16e93
		System.out.println(c5.getClassLoader());	//null

	}
}

注 c2,c3 调用的是loadClass()方法,该方法会调用我们自定义类加载器重写的findClass()方法。loadClass()方法执行过程见上图。也就是说,如果我们写的自定义类都调用loadClass()方法来加载类的话。可以将自定义类加载器//--------包围的那一部分省略,因为loadClass()方法会执行这些步骤,如果加载不到该类才会调用findClass()方法。

//	-----------------------
c = findLoadedClass(name);	//是否能在已加载类中找到
if(c != null) {
	return c;						//如果已加载,直接返回
}

ClassLoader parent = this.getParent();	//获得父类加载器,就是应用程序类加载器AppClassLoader
try {
	c = parent.loadClass(name);	//父类加载器加载该类,父类加载该类过程就使用了双亲委托机制
} catch (Exception e) {
	// e.printStackTrace();
}

if(c != null) {						// 如果父类加载器成功加载该类 
	return c;							// 返回父类加载器加载的类
}
//		-----------------------

当然,如果我们想调用自定义类的findClass()方法去加载如java.lang.String这样的类,那这些就必须写上。

附:自定义网络类加载器。改自文件类加载器。

package com.feng.ClassTest;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

/**
 * 网络类加载器
 * @author feng
 *
 */

// 传入网络url
// com.feng.ClassTest.User --> www.feng.cn/myjava/   com/feng/ClassTest/User.class
public class NetClassLoader extends ClassLoader {
	
	private String rootUrl;
	
	public NetClassLoader(String rootUrl) {
		this.rootUrl = rootUrl;
	}
	
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		
		Class<?> c = null;
		
		c = findLoadedClass(name);	//是否能在已加载类中找到
		if(c != null) {
			return c;						//如果已加载,直接返回
		}
		
		ClassLoader parent = this.getParent();	//获得父类加载器,就是应用程序类加载器AppClassLoader
		try {
			c = parent.loadClass(name);	//父类加载器加载该类,父类加载该类过程就使用了双亲委托机制
		} catch (Exception e) {
			// e.printStackTrace();
		}

		if(c != null) {						// 如果父类加载器成功加载该类 
			return c;							// 返回父类加载器加载的类
		}
		
		byte[] classData = getClassData(name);	// 自定义方法获取类字节码
		if(classData == null) {
			throw new ClassNotFoundException();	// 如果字节数组为空,抛出异常
		} else {
			c = defineClass(name, classData, 0, classData.length);
		}
		
		return c;
		
	}
	
	private byte[] getClassData(String classname) {
		// 拼出类class文件地址
		String path = rootUrl + "/" + classname.replace('.', '/') + ".class";
		
		InputStream is = null;
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			URL url = new URL(path);
			is = url.openStream();
			
			byte[] buffer = new byte[1024];
			int temp = 0;
			
			while((temp = is.read(buffer)) != -1) {
				baos.write(buffer, 0, temp);
			}
			
			return baos.toByteArray();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			return null;
		} finally {
			
			try {
				if(is != null) {
				is.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			try {
				if(baos != null) {
					baos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		
	}
	
}

基本一样。只是将地址换成了url,将文件读取换成了网络读取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值