1. Обзор
Java использует различные типы загрузчиков классов для загрузки ресурсов во время выполнения программы. В этом руководстве мы рассмотрим разницу в поведении загрузчиков текущих и потоковых классов в Java.
2. Что делает загрузчик классов?
Загрузчики классов Java находят и загружают классы, необходимые для выполнения приложения. Если запрошенный класс зависит от каких-либо других ресурсов, они также загружаются.
Нам нужны соответствующие загрузчики классов для загрузки различных типов классов всякий раз, когда это требуется Java-программами .
3. Связь между загрузчиками классов
Загрузчики классов Java следуют иерархическим отношениям .
Каждый запрос на поиск или загрузку класса делегируется соответствующему загрузчику родительского класса. Если все загрузчики классов-предков не могут найти класс, его пытается найти текущий загрузчик классов. Здесь «текущий класс» подразумевает класс выполняемого в данный момент метода.
Эта связь между загрузчиками классов помогает поддерживать уникальность ресурсов в приложении. Кроме того, если класс уже был загружен загрузчиком родительского класса, загрузчику дочернего класса не нужно перезагружать его.
4. Загрузчики классов по умолчанию
Загрузчики классов загружают классы и ресурсы, присутствующие в соответствующем пути к классам:
- Загрузчики классов системы или приложения загружают классы из пути к классам приложения.
- Загрузчики классов расширений выполняют поиск по пути к классам расширений (
JRE/lib/ext
) - Загрузчик классов Bootstrap просматривает путь к классам Bootstrap (
JRE/lib/rt.jar
)
Загрузчик классов Bootstrap или Primordial является родителем всех загрузчиков классов. Он загружает среду выполнения Java — классы, необходимые для запуска самой JVM.
Текущие загрузчики классов ищут ресурсы линейным иерархическим способом. Если загрузчик класса не может найти класс, он создает исключение java.lang.ClassNotFoundException
для соответствующего загрузчика дочернего класса. Затем загрузчик дочернего класса пытается найти класс.
Для сценариев, в которых требуемые ресурсы не найдены в путях к классам ни одного из загрузчиков классов в иерархии, в конечном результате мы получаем сообщения об ошибках, связанные с java.lang.ClassNotFoundException .
Мы также можем настроить поведение загрузки классов по умолчанию. Мы можем явно указать загрузчик класса при динамической загрузке класса .
Однако следует отметить, что если мы загружаем один и тот же класс из разных типов загрузчиков классов, JVM будет рассматривать их как разные ресурсы.
5. Загрузчики классов контекста
Помимо загрузчиков классов по умолчанию, J2SE также представила загрузчики классов контекста .
С каждым потоком в Java связан загрузчик класса контекста .
Мы можем получить доступ/изменить загрузчик класса контекста для потока, используя методы getContextClassLoader()
и setContextClassLoader() класса
Thread
.
Загрузчик класса контекста устанавливается во время создания потока. Если не указано явно, по умолчанию используется загрузчик класса контекста родительского потока .
Загрузчики классов контекста также следуют модели иерархии. Загрузчик корневого класса в данном случае является загрузчиком класса контекста исходного потока. Первичный поток — это первоначальный поток, созданный операционной системой.
Когда приложение начинает выполняться, могут создаваться другие потоки. Загрузчик классов контекста исходного потока изначально устанавливается на загрузчик классов, который загружает приложение, т. е. загрузчик системных классов.
Предположим, мы не обновляем загрузчик класса контекста для любого потока на любом уровне иерархии. В результате можно сказать, что по умолчанию загрузчик класса контекста для потока такой же, как загрузчик системного класса. Для таких сценариев, если мы выполним операции Thread.currentThread().getContextClassLoader()
и getClass().getClassLoader()
, обе вернут одни и те же объекты.
5.1. Решайте проблемы с делегированием
Загрузчики классов контекста важны, когда требуемые ресурсы отсутствуют в пути к классам загрузчиков классов Java по умолчанию. Поэтому мы можем использовать загрузчики классов контекста, чтобы отойти от традиционной модели линейного делегирования .
В иерархической модели загрузчиков классов ресурсы, загружаемые загрузчиками родительских классов, видны загрузчикам дочерних классов, но не наоборот. В некоторых сценариях загрузчикам родительских классов может потребоваться доступ к классам, присутствующим в пути к классам загрузчиков дочерних классов.
Загрузчики классов контекста — полезный инструмент для этого. Мы можем установить загрузчик класса контекста на желаемое значение при доступе к необходимым ресурсам. Следовательно, в приведенном выше случае мы можем использовать загрузчик класса контекста дочернего потока и можем найти ресурсы, присутствующие на уровне загрузчика дочернего класса.
5.2. Многомодульная среда
При установке свойства загрузчика класса контекста мы фактически переключаем контекст для загрузки ресурсов . Вместо поиска в текущем пути к классам мы получаем новый загрузчик классов, указывающий на другой путь к классам. Это особенно полезно, если мы хотим загрузить ресурсы из стороннего модуля или если мы работаем в среде с разными пространствами имен классов.
Однако здесь следует проявить осторожность и сбросить свойство загрузчика классов контекста обратно к исходному загрузчику классов, чтобы избежать каких-либо расхождений в будущем.
6. Заключение
В этой статье мы проанализировали значение использования загрузчиков классов контекста для загрузки ресурсов, недоступных через обычные загрузчики классов. Мы увидели, что можем также выбрать временное обновление загрузчика классов контекста для данного потока для загрузки необходимых классов.
Важно понимать контекст, в котором работает текущий метод. У нас могут быть ресурсы с одним и тем же именем, существующие в разных путях к классам. Следовательно, при загрузке ресурсов из нескольких загрузчиков классов мы должны проявлять осторожность.