Пользовательский загрузчик классов Java

Java JVM исходный код Шаблоны проектирования

Сценарии применения пользовательских загрузчиков классов

  • шифрование: если вы не хотите, чтобы ваш код был декомпилирован. (После того, как класс зашифрован, его больше нельзя загрузить с помощью ClassLoader. В этом случае вам нужно настроить загрузчик классов, чтобы сначала расшифровать класс, а затем загрузить его).

  • Загружать код из нестандартных источников: если ваш байт-код хранится в базе данных или даже в облаке, вам нужен собственный загрузчик классов для загрузки классов из указанного источника.

родительская делегация

  • Давайте сначала посмотримClassLoaderкласс по умолчаниюloadClassреализация метода
protected synchronized Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
        // First, check if the class has already been loaded
        Class c = findLoadedClass(name);
        if (c == null) {
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
                //父类加载器无法完成类加载请求
            }
            if (c == null) {
                // If still not found, then invoke findClass in order to find the class
                //子加载器进行类加载 
                c = findClass(name);
            }
        }
        if (resolve) {
            //判断是否需要链接过程,参数传入
            resolveClass(c);
        }
        return c;
    }

Рабочий процесс родительской модели делегирования выглядит следующим образом:

(1) Загрузчик классов запрашивает, был ли класс загружен из загруженного класса, и возвращается напрямую, если он загружен.

(2) Если класс не найден в загруженном классе, делегируйте загрузчику родительского класса для загрузкиc = parent.loadClass(name, false), родительский класс также будет использовать ту же стратегию, чтобы проверить, содержит ли класс, загруженный сам по себе, если нет, он делегирует родительскому классу и так далее, пока не будет загружен класс запуска.

(3) Если загрузчик класса запуска не загружается (например, в$JAVA_HOME/jre/libне найдено вclass), загрузчик класса расширения будет использоваться для попытки загрузки, и если он продолжит давать сбой, он будет использоватьсяAppClassLoaderдля загрузки, и если он продолжит давать сбой, будет выброшено исключениеClassNotFoundException, а затем вызвать текущий загрузчикfindClass()способ загрузки.

Преимущества родительского делегирования:

(1) Избегайте динамической замены классов, написанных вамиjavaосновные классы, такие какString.

(2) Избегайте повторной загрузки классов, потому чтоJVMСпособ различать разные классы основан не только на имени класса, тот жеclassФайлы загружаются разными загрузчиками классов, в результате чего получается два разных класса.

Основная тема: пользовательский загрузчик классов

Из приведенного выше исходного кода видно, что вызовclassLoaderПри загрузке он будет загружен в родительский класс согласно модели делегирования, и если загрузка не удалась, будет загружен текущий загрузчик.findClassспособ загрузки,
Таким образом, нашему пользовательскому загрузчику классов нужно только наследоватьClassLoader, и перезаписатьfindClassметод.

  • подготовить одинclassФайл, скомпилированный и помещенный в корневую директорию диска D
public class People {
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
  • пользовательский загрузчик классовMyClassLoader,наследоватьClassLoaderпокрытиеfindClassметод (гдеdefineClassметод преобразует файл, состоящий из байтов двоичного потока, вjava.lang.Class)
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
 
public class MyClassLoader extends ClassLoader
{
    public MyClassLoader(){}
    
    public MyClassLoader(ClassLoader parent)
    {
        super(parent);
    }
    
    protected Class<?> findClass(String name) throws ClassNotFoundException
    {
    	File file = new File("D:/People.class");
        try{
            byte[] bytes = getClassBytes(file);
            //defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class
            Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
            return c;
        } 
        catch (Exception e)
        {
            e.printStackTrace();
        }
        
        return super.findClass(name);
    }
    
    private byte[] getClassBytes(File file) throws Exception
    {
        // 这里要读入.class的字节,因此要使用字节流
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(baos);
        ByteBuffer by = ByteBuffer.allocate(1024);
        
        while (true){
            int i = fc.read(by);
            if (i == 0 || i == -1)
            break;
            by.flip();
            wbc.write(by);
            by.clear();
        }
        fis.close();
        return baos.toByteArray();
    }
}
  • Проверьте это в основной функции
MyClassLoader mcl = new MyClassLoader(); 
Class<?> clazz = Class.forName("People", true, mcl); 
Object obj = clazz.newInstance();

System.out.println(obj);
//打印出我们的自定义类加载器
System.out.println(obj.getClass().getClassLoader());


Ссылка на ссылку:
woo woo woo.cn blog on.com/excessive extravagance decoration/afraid/7…
blog.CSDN.net/Цвет U_Calvin/…