JVM类加载器-自定义实现类加载器

参考《深入理解java虚拟机》

双亲委派模式
从java虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器是由c++语言实现的,是虚拟机自身的一部分;另一种就是所有其它的类加载器,这些类加载器都是由java语言实现,独立于虚拟机外部,并且全都继承了抽象类java.lang.ClassLoader。
绝大部分程序都会使用到以下三种类加载器:
启动类加载器(  Bootstrap ClassLoader  ):这个类加载器负责将存放在/lib目录下的类库加载到虚拟机内存中,如:rt.jar;
扩展类加载器(Extension ClassLoader):这个类加载器负责加载/lib/ext目录下 *.jar;
应用类加载器(Application ClassLoader):这个类加载器负责加载用户类路径(classPath)上所指定的类库。
这些加载器之间的父子关系一般不会以继承来实现,而是使用组合关系复用父加载器的代码;这种层级关系被称为 双亲委派机制
双亲委派模型的工作过程是:如果一个类加载器收到类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的 启动类加载中,只用当父加载器反馈之间无法完成这个加载请求时,子类才会去尝试加载;
使用双金委派模型的好处是:java类随着它的加载器一起具备了一种带有优先级的层次关系,例如java.lang.Object,它存放在rt.jar之中,无论哪一个加载器都要加载这个类,最终都是委派给处于最顶端的启动类加载器,因此Object类在程序的各个类加载器环境中都是同一个类。相反如果没有双亲委派模型,由各个加载器去自行加载的话,如果用户之间编写了一个java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也就无法保障,应用程序也将会一片混乱;
 
双亲委派模型实现起来很简单,都集中在java.lang.ClassPath的loadClass()方法中,代码如下:
protected Class loadClass(String var1, boolean var2) throws ClassNotFoundException {
        synchronized(this.getClassLoadingLock(var1)) {
            // 首先检查请求的类是否已经被加载过
            Class var4 = this.findLoadedClass(var1);
            if(var4 == null) {
                long var5 = System.nanoTime();

                try {
                    if(this.parent != null) {
                        var4 = this.parent.loadClass(var1, false);
                    } else {
                        var4 = this.findBootstrapClassOrNull(var1);
                    }
                } catch (ClassNotFoundException var10) {
                    ;
                    // 如果父类加载器抛出ClassNotFoundException
                    // 说明父类加载器无法完成加载请求
                }

                if(var4 == null) {
                    long var7 = System.nanoTime();
                    // 在父类加载器无法加载的时候,再调用本身的findClass方法进行类加载
                    var4 = this.findClass(var1);
                    PerfCounter.getParentDelegationTime().addTime(var7 - var5);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(var7);
                    PerfCounter.getFindClasses().increment();
                }
            }

            if(var2) {
                this.resolveClass(var4);
            }

            return var4;
        }
    }
下面是自己写的 自定义类加载器
public class MyClassLoader extends ClassLoader {
	
	private String path;	// 加载器路径
	private String name;	// 类加载器名称
	
	public MyClassLoader(String name, String path){
		super();	// 应用加载器成为该类的父类加载器
		this.path = path;
		this.name = name;
	}
	
	public MyClassLoader(ClassLoader parent, String name, String path){
		super(parent);	//调用父类加载器的构造方法
		this.path = path;
		this.name = name;
	}

	/**
	 * 通过自定义ClassLoader加载自定义类
	 */
	@Override
	protected Class findClass(String name) throws ClassNotFoundException {
		byte[] data = readClassFileToByteArray(name);
		return this.defineClass(name, data, 0, data.length);
	}

	/**
	 * 获取.class字节码数组
	 * D:\test\com\jvm\demo.java
	 * @param name
	 * @return
	 */
	private byte[] readClassFileToByteArray(String name) {
		InputStream iStream = null;
		byte[] returnData = null;
		name = name.replaceAll("\\.", "/");
		
		String filePath = this.path + name + ".class";
		File file = new File(filePath);
		
		ByteArrayOutputStream oStream = new ByteArrayOutputStream();
		try {
			iStream = new FileInputStream(file);
			int tmp = 0;
			while ((tmp = iStream.read()) != -1) {	// 表示还存在
				oStream.write(tmp);
			}
			returnData = oStream.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				iStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return returnData;
	}
}
测试
package jvm;

public class Test {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		MyClassLoader loader = new MyClassLoader("test", "D:/tmp/");
		Class findClass = loader.loadClass("aaa");
		findClass.newInstance();
	}
	
}

自定义加载器分析的比较透彻的链接:JVM——自定义类加载器


你可能感兴趣的:(SpringBoot学习)