一、原理:使用不同类加载器,加载的同名类文件,在JVM中并是不同的Class对象

同一个类加载器,对于同一个类名(如:com.xxx.Test),只能加载一次

当输入:a时,类加载器为:

新实例化出来的HotSwapClassLoader加载器的父加载器==》HotSwapClassLoader.class.getClassLoader()

此处为:App class loader

当输入:b或者c时,类加载器为:

新实例化出来的HotSwapClassLoader加载器本身,加载方法为:

defineClass(null, classByte, 0,classByte.length)
defineClass(null, classByte, 0,classByte.length)


重点:每个类加载器对象,自身有一个已加载Class对象池

子加载器,委托父加载器,加载的,二者池中均有此类对象

父加载器自己加载的类对象,子加载器,可以自己另行再加载一个“同名”类对象




图 1. 类加载器树状组织结构示意图

java实现jar包热替换 java class热替换_python

二、类加载器的代理模式

类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。在介绍代理模式背后的动机之前,首先需要说明一下 Java 虚拟机是如何判定两个 Java 类是相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。

比如一个 Java 类 com.example.Sample,编译之后生成了字节代码文件 Sample.class。两个不同的类加载器ClassLoaderA和 ClassLoaderB分别读取了这个 Sample.class文件,并定义出两个 java.lang.Class类的实例来表示这个类。这两个实例是不相同的。对于 Java 虚拟机来说,它们是不同的类。试图对这两个类的对象进行相互赋值,会抛出运行时异常 ClassCastException。下面通过示例来具体说明。

三、示例共有三个类

1、主类

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;


public class Test {

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        HotSwapClassLoader classloader = new HotSwapClassLoader();

        while (true) {
            String str = br.readLine();

            if (str.equals("0")) {
                break;
            } else if (str.equals("a")) {
            } else {
                classloader = new HotSwapClassLoader();
                /**
                 * 以下注释为,本次测试重点信息
                 */
                // 下面一行:类加载器在尝试自己去查找某个类的字节代码并定义它时,
                // 会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推
                // 也就是说:如存在此行时,下一行,就报错
                // java.lang.LinkageError: loader (instance of
                // HotSwapClassLoader): attempted duplicate class definition for
                // name: "MainTest"

                // Class clazz = Class.forName("MainTest", true, classloader);
                classloader.loadByPath("C:/Users/michael/Desktop/file/temp/" + str + "/MainTest.class");
            }

            Class clazz = Class.forName("MainTest", true, classloader);

            Method method = clazz.getMethod("main", new Class[] { String[].class });
            method.invoke(null, new String[] { null });
        }
    }

}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;


public class Test {

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        HotSwapClassLoader classloader = new HotSwapClassLoader();

        while (true) {
            String str = br.readLine();

            if (str.equals("0")) {
                break;
            } else if (str.equals("a")) {
            } else {
                classloader = new HotSwapClassLoader();
                /**
                 * 以下注释为,本次测试重点信息
                 */
                // 下面一行:类加载器在尝试自己去查找某个类的字节代码并定义它时,
                // 会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推
                // 也就是说:如存在此行时,下一行,就报错
                // java.lang.LinkageError: loader (instance of
                // HotSwapClassLoader): attempted duplicate class definition for
                // name: "MainTest"

                // Class clazz = Class.forName("MainTest", true, classloader);
                classloader.loadByPath("C:/Users/michael/Desktop/file/temp/" + str + "/MainTest.class");
            }

            Class clazz = Class.forName("MainTest", true, classloader);

            Method method = clazz.getMethod("main", new Class[] { String[].class });
            method.invoke(null, new String[] { null });
        }
    }

}

2、类加载器

import java.io.FileInputStream;
import java.io.InputStream;

public class HotSwapClassLoader extends ClassLoader {
    public HotSwapClassLoader() {
        super(HotSwapClassLoader.class.getClassLoader());
        System.out.println(HotSwapClassLoader.class.getClassLoader());
    }

    public Class loadByte(byte[] classByte) {
        return defineClass(null, classByte, 0, classByte.length);
    }

    public Class loadByPath(String filePath) throws Exception {
        InputStream is = new FileInputStream(filePath);

        byte[] b = new byte[is.available()];

        is.read(b);
        is.close();

        return defineClass(null, b, 0, b.length);
    }
}
import java.io.FileInputStream;
import java.io.InputStream;

public class HotSwapClassLoader extends ClassLoader {
    public HotSwapClassLoader() {
        super(HotSwapClassLoader.class.getClassLoader());
        System.out.println(HotSwapClassLoader.class.getClassLoader());
    }

    public Class loadByte(byte[] classByte) {
        return defineClass(null, classByte, 0, classByte.length);
    }

    public Class loadByPath(String filePath) throws Exception {
        InputStream is = new FileInputStream(filePath);

        byte[] b = new byte[is.available()];

        is.read(b);
        is.close();

        return defineClass(null, b, 0, b.length);
    }
}

3、需要热替换的测试类

public class MainTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("AAAAAAAAAAAAAAAAAAA");
	}

}
public class MainTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("AAAAAAAAAAAAAAAAAAA");
	}

}