что такое classloader и для чего используется
Загрузка классов в Java. Теория
Одной из основных особенностей платформы Java является модель динамической загрузки классов, которая позволяет загружать исполняемый код в JRE не перезагружая основое приложение. Такая особенность широко используется в серверах приложений, получивших последнее время высокую популярность.
В статье рассмотрены базовые понятия, аспекты и принципы модели динамической загрузки кода. В следующей статье будет рассмотрена реализация собственного загрузчика классов, как основного механизма приложения с плагино-модульной архитектурой.
Введение
Классы загружаются по мере надобности, за небольшим исключением. Некоторые базовые классы из rt.jar (java.lang.* в частности) загружаются при старте приложения. Классы расширений ($JAVA_HOME/lib/ext), пользовательские и большинство системных классов загружаются по мере их использования.
Виды загрузчиков
Различают 3-и вида загрузчиков в Java. Это — базовый загрузчик (bootstrap), системный загрузчик (System Classloader), загрузчик расширений (Extension Classloader).
System Classloader — системный загрузчик, реализованный уже на уровне JRE. В Sun JRE — это класс sun.misc.Launcher$AppClassLoader. Этим загрузчиком загружаются классы, пути к которым указаны в переменной окружения CLASSPATH.
Управлять загрузкой расширений можно с помощью системной опции java.ext.dirs.
Понятия
Различают текущий загрузчик (Current Classloader) и загрузчик контекста (Context Classloader).
Current Classloader — это загрузчик класса, код которого в данный момент исполняется. Текущий загрузчик используется по умолчанию для загрузки классов в процессе исполнения. В часности, при использовании метода Class.forName(«»)/ClassLoader.loadClass(«») или при любой декларации класса, ранее не загруженного.
Context Classloader — загрузчик контекста текущего потока. Получить и установить данный загрузчик можно с помощью методов Thread.getContextClassLoader()/Thread.setContextClassLoader(). Загрузчик контекста устанавливается автоматически для каждого нового потока. При этом, используется загрузчик родительского потока.
Модель делегирования загрузки
Начиная с версии Java 2 Platform, Standard Edition, v1.2 загрузчики классов образуют иерархию. Корневым является базовый (у него предка нет). Все остальные загрузчики при инициализации инстанциируют ссылку на родительский загрузчик. Такая иерархия необходима для модели делегирования загрузки. В общем случа, иерархия выглядит следующим образом.
Право загрузки класса рекурсивно делегируется от самого нижнего загрузчика в иерархии к самому верхнему. Такой подход позволяет загружать классы тем загрузчиком, который максимально близко находится к базовому. Так достигается максимальная область видимости классов. Под областью видимости подразумевается следующее. Каждый загрузчик ведет учет классов, которые были им загружены. Множество этих классов и назвается областью видимости.
Рассмотрим процесс загрузки более детально. Пусть в систем исполнения встретилась декларация переменной пользовательского класс Student.
1) Системный загрузчик попытается поискать в кеше класс Student.
_1.1) Если класс найден, загрузка окончена.
_1.2) Если класс не найден, загрузка делегируется загрузчику расширений.
2) Загрузчик расширений попытается поискать в кеше класс Student.
_2.1) Если класс найден, загрузка окончена.
_2.2) Если класс не найден, загрузка делегируется базовому загрузчику.
3) Базовый загрузчик попытается поискать в кеше класс Student.
_3.1) Если класс найден, загрузка окончена.
_3.2) Если класс не найден, базовый загрузчик попытается его загрузить.
__3.2.1) Если загрузка прошла успешно, она закончена 😉
__3.2.2) Иначе управление предается загрузчику раширений.
_3.3) Загрузчик расширений пытается загрузить класс.
__3.3.1) Если загрузка прошла успешно, она закончена 😉
__3.3.2) Иначе управление предается системному загрузчику.
_3.4) Системный загрузчик пытается загрузить класс.
__3.4.1) Если загрузка прошла успешно, она закончена 😉
__3.4.2) Иначе генерируется исключение java.lang.ClassNotFoundException.
Если в системе присутствуют пользовательские загрузчики, они должны
а) расширять класс java.lang.ClassLoader;
б) поддерживать модель динамической загрузки.
Inside
public class B extends A
public class C extends B
Вывод показывает, что классы были загружены не в том порядке в котором были использованы. Это обусловлено наследованием.
[Loaded Main from file:/C:/devel/CL/bin/]
[Loaded A from file:/C:/devel/CL/bin/]
[Loaded B from file:/C:/devel/CL/bin/]
[Loaded C from file:/C:/devel/CL/bin/]
Внутренности JVM, Часть 1 — Загрузчик классов
Перевод статьи подготовлен специально для студентов курса «Разработчик Java».
В этой серии статей я расскажу о том, как работает Java Virtual Machine. Сегодня мы рассмотрим механизм загрузки классов в JVM.
Виртуальная машина Java — это сердце экосистемы Java-технологий. Она делает для Java-программ возможность реализации принципа «написано один раз, работает везде» (write once run everywhere). Как и другие виртуальные машины, JVM представляет собой абстрактный компьютер. Основная задача JVM — загружать class-файлы и выполнять содержащийся в них байт-код.
В состав JVM входят различные компоненты, такие как загрузчик классов (Classloader), сборщик мусора (Garbage Collector) (автоматическое управление памятью), интерпретатор, JIT-компилятор, компоненты управления потоками. В этой статье рассмотрим загрузчик классов (Class loader).
Загрузчик классов загружает class-файлы как для вашего приложения, так и для Java API. В виртуальную машину загружаются только те class-файлы Java API, которые действительно требуются при выполнении программы.
Байт-код выполняется подсистемой исполнения (execution engine).
Что такое загрузка классов?
Загрузка классов — это поиск и загрузка типов (классов и интерфейсов) динамически во время выполнения программы. Данные о типах находятся в бинарных class-файлах.
Этапы загрузки классов
Подсистема загрузчика классов отвечает не только за поиск и импорт бинарных данных класса. Она также выполняет проверку правильности импортируемых классов, выделяет и инициализирует память для переменных класса, помогает в разрешении символьных ссылок. Эти действия выполняются в следующем порядке:
Примечание — загрузчик классов, помимо загрузки классов, также отвечает за поиск ресурсов. Ресурс — это некоторые данные (например, “.class” файл, данные конфигурации, изображения), которые идентифицируются с помощью абстрактного пути, разделенного символом «/». Ресурсы обычно упаковываются вместе с приложением или библиотекой для того, чтобы их можно было использовать в коде приложения или библиотеки.
Механизм загрузки классов в Java
Platform class loader — загружает выбранные (на основе безопасности / разрешений) модули Java SE и JDK. Например, java.sql.
Bootstrap class loader — загружает основные модули Java SE и JDK.
Эти три встроенных загрузчика классов работают вместе следующим образом:
Запустив этот код на установленном у меня Amazon Corretto 11.0.3, получим следующий результат:
Подробнее изучить ClassLoader API вы можете здесь (JDK 11).
ClassLoader в Java
ClassLoader в Java вызывается средой выполнения для динамической загрузки классов, когда это требуется приложением на виртуальной машине. Поскольку загрузчики классов являются частью среды выполнения, виртуальная машина не будет иметь никакого представления о базовых файлах и файловых системах.
Расширение
Загружает расширения основных классов Java из библиотеки расширений JDK. Он является дочерним элементом Bootstrap и загружает расширения из каталога JRE / lib / text или любого другого каталога, указанного в системном свойстве java.ext.dirs.
Приложение или Система
Загрузчик
Как все мы знаем, что Java-классы загружаются экземпляром java.lang.ClassLoade. Но поскольку ClassLoaders являются классами, Bootstrap отвечает за загрузку внутренних классов JDK. Представляет собой машинный код, который запускает операцию, когда JVM вызывает ее и загружает классы из rt.jar. Таким образом, вы можете понять, что служба Bootstrap не имеет родительского ClassLoader и, таким образом, называется Primordial ClassLoader.
Примечание. Приоритет Bootstrap выше, чем Extension, а приоритет, присвоенный Extension ClassLoader, выше, чем Application ClassLoader. Обратитесь к изображению ниже:
Принципы
Набор правил, на основе которых работает Java ClassLoader, состоит из следующих трех принципов:
Свойство уникальности
Это свойство гарантирует, что нет повторения классов, и все классы являются уникальными. Свойство уникальности также гарантирует, что классы, загружаемые родительским ClassLoader, не загружаются дочерним. В сценарии, где родительский ClassLoader не может найти класс, текущий экземпляр попытается сделать это самостоятельно.
Модель делегирования
ClassLoader работает на основе набора операций, заданных моделью делегирования. Таким образом, всякий раз, когда генерируется запрос на поиск класса или ресурса, экземпляр ClassLoader делегирует поиск класса или ресурса родительскому ClassLoader.
Набор операций, на основе которых работает ClassLoader:
Обратитесь к изображению ниже.
Принцип видимости
Согласно этому принципу, дочерние классы видны для классов, загруженных их родительскими ClassLoaders, но наоборот не соответствует действительности. Таким образом, классы, загруженные приложением ClassLoader, имеют видимость классов, загруженных расширением и загрузчиком ClassLoader.
Например, если у нас есть два класса: AB, предположим, что класс A загружается приложением ClassLoader, а класс B загружается расширением ClassLoader. Здесь классы A и B видны всем тем классам, которые загружены приложением ClassLoader, но класс B виден только тем классам, которые загружены расширением ClassLoader.
Кроме того, если вы попытаетесь загрузить эти классы с помощью загрузчика ClassLoader, вы увидите. исключение.
Методы
Вот несколько основных методов ClassLoader:
loadClass (имя строки, логическое разрешение)
Этот метод является точкой входа ClassLoader и используется для загрузки класса, на который ссылается JVM. Он принимает имя класса в качестве параметра. JVM вызывает метод loadClass() для разрешения ссылок на класс, устанавливая логическое значение true. Только если нам нужно определить, существует ли класс или нет, логический параметр имеет значение false.
defineClass()
Последний метод, используемый для определения массива байтов как экземпляра класса. В случае, если класс недопустим, он выдает ClassFormatError.
findClass (Строковое имя)
Используется для поиска указанного класса. Таким образом, он просто находит класс с полностью определенным именем в качестве параметра, но не загружает класс. Метод loadClass() вызывает этот метод, если родительский ClassLoader не может найти запрошенный класс. Кроме того, если ни один из родителей ClassLoader не находит класс, реализация по умолчанию генерирует исключение ClassNotFoundException.
Class.forName (имя строки, логическая инициализация, загрузчик ClassLoader)
Этот метод используется для загрузки и инициализации класса. Это дает возможность выбрать любой из ClassLoaders и в случае, если параметр ClassLoader равен NULL, тогда автоматически используется Bootstrap ClassLoader.
GetParent()
Используется для возврата родительского ClassLoader для делегирования.
getResource()
Пытается найти ресурс с заданным именем. Первоначально он делегирует запрос родительскому ClassLoader для ресурса. Если родительский объект имеет значение null, то ищется путь к ClassLoader, встроенному в JVM. Теперь, если это не удается, тогда метод вызовет findResource(String), чтобы найти ресурс, где имя ресурса указывается в качестве входных данных, которые могут быть как абсолютным, так и относительным путем к классу. Затем он возвращает объект URL для чтения ресурса или возвращает нулевое значение, если у ресурса нет достаточных прав для возврата ресурса или он не найден.
Пользовательский
Встроенные ClassLoaders позаботятся о большинстве случаев, когда файлы уже находятся в файловой системе, но если вы хотите загрузить классы с локального жесткого диска, вам нужно использовать пользовательские ClassLoaders.
Чтобы создать собственный ClassLoader, вам нужно расширить класс ClassLoader и переопределить метод findClass():
Средняя оценка / 5. Количество голосов:
Или поделись статьей
Видим, что вы не нашли ответ на свой вопрос.
Подробно про ClassLoader в Java – примеры
Работая на Java, мы часто используем большое количество классов. Они не загружаются все сразу в память, вместо этого загружаются, когда требуется приложением. Тогда и требуется ClassLoaders.
Что такое ClassLoader в Java?
ClassLoader в Java вызывается средой выполнения для динамической загрузки классов, когда это требуется приложением на виртуальной машине. Поскольку ClassLoaders являются частью Java Runtime Environment, виртуальная машина не будет иметь никакого представления о базовых файлах и файловых системах.
Расширение
Как следует из названия, загружает расширения основных классов Java из библиотеки расширений JDK. Он является дочерним элементом загрузчика Bootstrap ClassLoader и загружает расширения из каталога JRE / lib / text или любого другого каталога, указанного в системном свойстве java.ext.dirs.
Приложение или система
Загрузчик
Как все мы знаем, что Java-классы загружаются экземпляром java.lang.ClassLoade. Но поскольку ClassLoaders являются классами, Bootstrap ClassLoader отвечает за загрузку внутренних классов JDK. По сути, это машинный код, который запускает операцию, когда JVM вызывает ее и загружает классы из rt.jar. Таким образом, вы можете понять, что служба Bootstrap ClassLoader не имеет родительского ClassLoader и поэтому известна как Primordial ClassLoader.
Примечание. Приоритет Bootstrap выше, чем Extension, а приоритет, присвоенный Extension ClassLoader, выше, чем Application ClassLoader. Обратитесь к изображению ниже:
Принципы
Набор правил, на основе которых работает ClassLoader, состоит из следующих трех принципов:
Свойство уникальности
Это свойство гарантирует, что нет повторения классов, и все классы являются уникальными. Также гарантирует, что классы, загружаемые родительским ClassLoader, не загружаются дочерним. В сценарии, где родительский ClassLoader не может найти класс, текущий экземпляр попытается сделать это сам.
Модель делегирования
ClassLoader работает на основе набора операций, заданных моделью делегирования. Таким образом, всякий раз, когда генерируется запрос на поиск класса или ресурса, экземпляр ClassLoader делегирует поиск класса или ресурса родительскому ClassLoader.
Набор операций, на основе которых работает ClassLoader:
Обратитесь к изображению ниже.
Принцип видимости
Согласно этому принципу, дочерние классы видны для классов, загруженных их родительскими ClassLoaders, но наоборот не соответствует действительности. Таким образом, классы, загруженные Application, имеют видимость классов, загруженных Extension и Bootstrap.
Например, если у нас есть два класса: A и B, предположим, что класс A загружается Application, а класс B загружается Extension. Здесь классы A и B видны всем тем классам, которые загружены Application, но класс B виден только тем классам, которые загружены Extension.
Кроме того, если вы попытаетесь загрузить эти классы с помощью Bootstrap ClassLoader, вы увидите java.lang.ClassNotFoundException исключение.
Методы
Вот несколько основных методов:
loadClass()
Этот метод является точкой входа ClassLoader и используется для загрузки класса, на который ссылается JVM. Он принимает имя класса в качестве параметра. JVM вызывает метод loadClass() для разрешения ссылок на класс, устанавливая логическое значение true. Только если нам нужно определить, существует ли класс или нет, логический параметр имеет значение false.
defineClass()
Последний метод, используемый для определения массива байтов как экземпляра класса. В случае, если класс является недействительным, он выдает ClassFormatError.
findClass ()
Метод используется для поиска указанного класса. Таким образом, он просто находит класс с полностью определенным именем в качестве параметра, но не загружает его. Метод loadClass() вызывает этот метод, если родительский ClassLoader не может найти запрошенный класс. Кроме того, если ни один из родителей ClassLoader не находит класс, реализация по умолчанию генерирует исключение ClassNotFoundException.
Class.forName ()
Этот метод используется для загрузки и инициализации класса. Это дает возможность выбрать любой из ClassLoaders и в случае, если параметр ClassLoader равен NULL, тогда автоматически используется Bootstrap ClassLoader.
GetParent()
Метод getParent используется для возврата родительского ClassLoader для делегирования.
getResource()
Метод getResource() пытается найти ресурс с заданным именем. Первоначально он делегирует запрос родительскому ClassLoader для ресурса. В случае, если родительский элемент является нулевым, ищется путь к ClassLoader, встроенному в JVM.
Теперь, если это не удается, тогда метод вызовет findResource (), чтобы найти ресурс, где имя ресурса указывается как вход, который может быть как абсолютным, так и относительным путем к классу. Затем он возвращает объект URL для чтения ресурса или возвращает нулевое значение, если у ресурса нет соответствующих прав для возврата ресурса или он не найден.
Пользовательский ClassLoader в Java
Встроенные ClassLoaders позаботятся о большинстве случаев, когда файлы уже находятся в файловой системе, но если вы хотите загрузить классы с локального жесткого диска, вам нужно использовать пользовательские ClassLoaders.
Как создать
Чтобы создать, вам нужно расширить класс ClassLoader и переопределить метод findClass().
Пример: Давайте создадим собственный ClassLoader, который расширяет стандартный ClassLoader и загружает байтовый массив из указанного файла.
Как происходит загрузка классов в JVM
Загрузчик классов
загрузка байт-кода из ресурсов и создание экземпляра класса Class
сюда входит поиск запрошенного класса среди загруженных ранее, получение байт-кода для загрузки и проверка его корректности, создание экземпляра класса Class (для работы с ним в runtime), загрузка родительских классов. Если родительские классы и интерфейсы не были загружены, то и рассматриваемый класс считается не загруженным.
связывание (или линковка)
по спецификации этот этап разбивается еще на три стадии:
инициализация полученного объекта
здесь, в отличие от предыдущих пунктов, вроде бы все понятно, что должно происходить. Было бы, конечно, интересно разобраться как именно это происходит.
Типы загрузчиков Java
Bootstrap – базовый загрузчик, также называется Primordial ClassLoader.
загружает стандартные классы JDK из архива rt.jar
Extension ClassLoader – загрузчик расширений.
загружает классы расширений, которые по умолчанию находятся в каталоге jre/lib/ext, но могут быть заданы системным свойством java.ext.dirs
System ClassLoader – системный загрузчик.
загружает классы приложения, определенные в переменной среды окружения CLASSPATH
В Java используется иерархия загрузчиков классов, где корневым, разумеется, является базовый. Далее следует загрузчик расширений, а за ним уже системный. Естественно, каждый загрузчик хранит указатель на родительский для того, чтобы смочь делегировать ему загрузку в том случае, если сам будет не в состоянии этого сделать.
Абстрактный класс ClassLoader
Три принципа загрузки классов
Делегирование
Запрос на загрузку класса передается родительскому загрузчику, и попытка загрузить класс самостоятельно выполняется, только если родительский загрузчик не смог найти и загрузить класс. Такой подход позволяет загружать классы тем загрузчиком, который максимально близко находится к базовому. Так достигается максимальная область видимости классов. Каждый загрузчик ведет учет классов, которые были загружены именно им, помещая их в свой кэш. Множество этих классов и называется областью видимости.
Видимость
Загрузчик видит только «свои» классы и классы «родителя» и понятия не имеет о классах, которые были загружены его «потомком».
Уникальность
Класс может быть загружен только однажды. Механизм делегирования позволяет убедиться, что загрузчик, инициирующий загрузку класса, не перегрузит загруженный ранее в JVM класс.
Таким образом, при написании своего загрузчика разработчик должен руководствоваться этими тремя принципами.