《深入理解java虚拟机》之简单聊聊自定义类加载器

首先说我们写的java代码(xxx.java文件)如何被jvm使用?

  通过javac 编译为可被虚拟机识别的机器码(二进制),然后通过类加载器将二进制文件放入内存,这个过程称为内的加载过程,而加载类(xxx.java文件)的类被称作类加载器,实际也是有java代码写的一个类(除了引导类加载器,该加载器有C++编写)

1、类加载器结构图如下(该图来自于网络 https://blog.csdn.net/p10010/article/details/50448491 )

可以看到,自定义类加载器在最下面,表示当一个类需要使用用户自定义类加载器时,先把这个类交给其父类即应用程序类加载器,当 应用程序类加载器 拿到这个类后不管三七二十一,先交给自己的"父类"即扩展类加载器,扩展类加载器一样,先交给启动类加载器,当然启动类加载器是最顶级的加载器了,自然需要看看自己能不能加载这个类,如果能就自己加载并把加载后的结果(java.lang,Calss对象)返回,否则给自己的子类让其加载,以此类推,这就是双亲委托机制

其好处是安全,各个类加载的主要加载对象如下:

启动类加载器只加载特定位置的类(%JAVA_HOME%\jre\lib\rt.jar)里面的类(这也是java的核心类库)

扩展类加载器只加载 %JAVA_HOME%\jre\lib\ext\下的类,ext=>extend

应用类加载器在一般情况下回加载其他所有类路径下的类 

用户自定义类加载器用于特定功能的类加载(比如要加密某个类)

2、类加载的流程如下(图片来自网络https://www.cnblogs.com/wxd0108/p/6681618.html)

 

 

3、具体代码如下

package com.lyzx.jvm;

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

public class MyClassLoad extends ClassLoader{
	private final String rootDir;
	
	public MyClassLoad(String rootDir){
		this.rootDir = rootDir;
	}
	
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		System.out.println("调用findClass("+name+")");
		Class<?> c = findLoadedClass(name);
		
		//应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
		if(c != null){
			System.out.println("类"+name+"已经被加载,不需要重复加载!");
			return c;
		}else{
			ClassLoader parent = this.getParent();
			try {
				c = parent.loadClass(name);	   //委派给父类加载
			} catch (Exception e){
				System.out.println("父类没有加载到["+name+"]这个类");
				e.printStackTrace();
			}
			
			if(c != null){
				System.out.println("父类加载器加载到["+name+"]这个类");
				return c;
			}else{
				byte[] classData = getClassData(name);
				if(classData == null){
					throw new ClassNotFoundException();
				}else{
					c = defineClass(name, classData, 0,classData.length);
					System.out.println("自定义类加载器加载类["+name+"]");
				}
			}
		}
		return c;
	}
	
	private byte[] getClassData(String classname){   //com.bjsxt.test.User   d:/myjava/  com/bjsxt/test/User.class
		String path = rootDir +"/"+ classname.replace('.', '/')+".class";
		
//		IOUtils,可以使用它将流中的数据转成字节数组
		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){
			e.printStackTrace();
			return null;
		}finally{
			try {
				if(is!=null){
					is.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(baos!=null){
					baos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

测试代码如下

package com.lyzx.jvm;

public class JvmTest {
	
	public static void main(String[] args) throws ClassNotFoundException {
		MyClassLoad loader = new MyClassLoad("D:\\testJvm");
		Class<?> c = loader.loadClass("com.lyzx.jvm.JvmLearn");
		System.out.println(">>>"+c.getClassLoader());
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值