Поиск…


замечания

Класс loader - это класс, основной целью которого является опосредование местоположения и загрузки классов, используемых приложением. Погрузчик классов также может находить и загружать ресурсы .

Стандартные классы classloader могут загружать классы и ресурсы из каталогов в файловой системе и из JAR и ZIP-файлов. Они также могут загружать и кэшировать файлы JAR и ZIP с удаленного сервера.

Классовые загрузчики обычно закодированы, так что JVM будет пытаться загружать классы из стандартных библиотек классов, предпочитая источники, предоставленные приложением. Пользовательские загрузчики классов позволяют программисту изменять это. Также можно делать такие вещи, как дешифрование файлов байт-кода и модификация байт-кода.

Создание и использование загрузчика классов

Этот базовый пример показывает, как приложение может создавать экземпляр класса loader и использовать его для динамической загрузки класса.

URL[] urls = new URL[] {new URL("file:/home/me/extras.jar")};
Classloader loader = new URLClassLoader(urls);
Class<?> myObjectClass = loader.findClass("com.example.MyObject");

Созданный в этом примере загрузчик классов будет иметь загрузчик по умолчанию как родительский, и сначала попытается найти любой класс в родительском загрузчике классов, прежде чем смотреть в «extra.jar». Если запрошенный класс уже загружен, вызов findClass вернет ссылку на ранее загруженный класс.

findClass может завершиться неудачей различными способами. Наиболее распространенными являются:

  • Если названный класс не может быть найден, вызов с throw ClassNotFoundException .
  • Если названный класс зависит от какого-либо другого класса, который не может быть найден, вызов вызовет NoClassDefFoundError .

Внедрение пользовательского классаLoader

Каждый пользовательский загрузчик должен прямо или косвенно расширить класс java.lang.ClassLoader . Основными точками расширения являются следующие методы:

  • findClass(String) - перегружает этот метод, если ваш загрузчик классов следует стандартной модели делегирования для загрузки классов.
  • loadClass(String, boolean) - перегружает этот метод для реализации альтернативной модели делегирования.
  • findResource и findResources - перегружать эти методы для настройки загрузки ресурсов.

Методы defineClass которые отвечают за фактическую загрузку класса из массива байтов, являются final чтобы предотвратить перегрузку. Перед вызовом defineClass .

Вот простой, который загружает определенный класс из массива байтов:

public class ByteArrayClassLoader extends ClassLoader {
    private String classname;
    private byte[] classfile;

    public ByteArrayClassLoader(String classname, byte[] classfile) {
        this.classname = classname;
        this.classfile = classfile.clone();
    }

    @Override
    protected Class findClass(String classname) throws ClassNotFoundException {
        if (classname.equals(this.classname)) {
            return defineClass(classname, classfile, 0, classfile.length);
        } else {
            throw new ClassNotFoundException(classname);
        }
    }
}

Поскольку мы только переопределили метод findClass , этот пользовательский загрузчик классов будет вести себя следующим образом при вызове loadClass .

  1. Метод loadClass findLoadedClass вызывает findLoadedClass чтобы узнать, был ли класс с этим именем уже загружен этим загрузчиком классов. Если это удастся, результирующий объект Class возвращается запрашивающему.
  2. Метод loadClass затем передает родительскому загрузчику классов, вызывая его вызов loadClass . Если родитель может обработать запрос, он вернет объект Class который затем возвращается запрашивающему.
  3. Если родительский загрузчик классов не может загрузить класс, findClass затем вызывает наш метод override findClass , передавая имя загружаемого класса.
  4. Если запрашиваемое имя соответствует this.classname , мы вызываем defineClass для загрузки фактического класса из this.classfile байтов this.classfile . Затем возвращается возвращаемый объект Class .
  5. Если имя не ClassNotFoundException , мы бросаем ClassNotFoundException .

Загрузка внешнего файла .class

Чтобы загрузить класс, мы сначала должны его определить. Класс определяется ClassLoader . Есть только одна проблема, Oracle не написала код ClassLoader с этой доступной функцией. Чтобы определить класс, нам нужно получить доступ к методу с именем defineClass() который является приватным методом класса ClassLoader .

Чтобы получить доступ к нему, мы создадим новый класс ByteClassLoader и распространим его на ClassLoader . Теперь, когда мы расширили наш класс до ClassLoader , мы можем получить доступ к приватным методам ClassLoader . Чтобы сделать defineClass() доступным, мы создадим новый метод, который будет действовать как зеркало для частного defineClass() . Для вызова частного метода нам потребуется имя класса, name , класс байт, classBytes , смещенные первый байт, который будет 0 , поскольку classBytes данные 'начинается в classBytes[0] , и смещение последнего байта, который будет classBytes.lenght потому что он представляет собой размер данных, который будет последним смещением.

public class ByteClassLoader extends ClassLoader {

    public Class<?> defineClass(String name, byte[] classBytes) {
        return defineClass(name, classBytes, 0, classBytes.length);
    }

}

Теперь у нас есть общедоступный defineClass() . Его можно вызвать, передав имя класса и байты класса в качестве аргументов.

Допустим, у нас есть класс с именем MyClass в пакете stackoverflow ...

Для вызова метода нам нужны байты класса, поэтому мы создаем объект Path представляющий Path нашего класса, используя метод Paths.get() и передавая путь двоичного класса в качестве аргумента. Теперь мы можем получить байты класса с Files.readAllBytes(path) . Поэтому мы создаем экземпляр ByteClassLoader и используем метод, который мы создали, defineClass() . У нас уже есть байты класса, но для вызова нашего метода нам также нужно имя класса, которое дается именем пакета (точка) канонического имени класса, в данном случае stackoverflow.MyClass .

Path path = Paths.get("MyClass.class");

ByteClassLoader loader = new ByteClassLoader();
loader.defineClass("stackoverflow.MyClass", Files.readAllBytes(path);

Примечание . Метод defineClass() возвращает объект Class<?> . Вы можете сохранить его, если хотите.

Чтобы загрузить класс, мы просто вызываем loadClass() и передаем имя класса. Этот метод может генерировать ClassNotFoundException поэтому нам нужно использовать блок cath try

try{
    loader.loadClass("stackoverflow.MyClass");
} catch(ClassNotFoundException e){
    e.printStackTrace();
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow