- Класслоадер находит файл класса и загружает его в JVM, чтобы программа могла его использовать.
- Классы загружаются при первом использовании, а не при запуске, и загрузчик находит байткод каждый раз.
- Встроенные загрузчики образуют цепочку родительских элементов (загрузчик начальной загрузки, платформенный, приложение), которая делегирует задачи вверх.
- Пользовательские класслоадеры позволяют модам Minecraft изменять классы игры во время их загрузки.
Загрузчик классов — это часть Java runtime, которая находит файл класса и загружает его в JVM, чтобы программа могла его использовать. Классы не загружаются все сразу при запуске. JVM загружает каждый класс в первый раз, когда он необходим, а загрузчик классов отвечает за поиск байт-кода, его чтение и передачу в JVM. Minecraft Java Edition работает именно на этом механизме.
Где используется Opal
Opal — это мод для Fabric, а Fabric загружает игру через собственный загрузчик классов, чтобы моды могли изменять классы во время их загрузки. Руководство по настройке описывает процесс установки.
Что на самом деле делает загрузчик классов
Загрузчик классов преобразует имя класса в используемый класс внутри работающей JVM. Вы запрашиваете тип, загрузчик находит соответствующие байты .class, и JVM определяет этот тип в памяти.
Работа происходит в три этапа. Загрузчик находит байткод для класса с заданным именем. JVM проверяет и связывает его. Затем класс готов к использованию. Именно поэтому Java-программа может подключать код, который никогда не упоминался во время компиляции: имени достаточно, чтобы загрузчик нашёл его
Иерархия загрузчиков
Загрузчики классов организованы в цепочку родитель-потомок, и большинство из них сначала обращаются к своему родителю. Стандартное приложение имеет три встроенных загрузчика, расположенных друг над другом.
| Загрузчик | Загружает |
|---|---|
| Bootstrap | Основные классы Java runtime |
| Platform | Стандартные модули библиотеки, находящиеся вне ядра |
| Application | Собственные классы вашей программы из classpath |
Когда загрузчику приложения запрашивается класс, он обычно сначала делегирует запрос выше по иерархии. Родитель получает возможность загрузить его, и только если ни один родитель не может этого сделать, потомок пытается. Это называется моделью делегирования, и она обеспечивает согласованность основных типов по всему приложению
Классовая идентичность и собственные загрузчики
Класс идентифицируется как по его имени, так и по загрузчику, который его определил. Два загрузчика могут загружать класс с одинаковым именем, и JVM рассматривает их как два разных типа. Это может показаться деталью, но именно это делает загрузчики классов такими мощными.
Программы могут определять свои собственные загрузчики классов. Пользовательский загрузчик может читать байт-код из любого места: из JAR-файла, сети, сгенерированного массива байтов, даже из байт-кода, который он преобразовал по пути. Именно так работают системы плагинов, серверы приложений и фреймворки модов, загружающие код, о котором основная программа не знала во время сборки.
Почему это важно для модов Minecraft
Моды существуют благодаря пользовательской загрузке классов. Загрузчик модов для Minecraft запускает игру через свой собственный загрузчик, чтобы он мог видеть и изменять игровые классы во время их загрузки. Именно в этой точке происходит исправление байт-кода.
Когда загрузчик находится между диском и JVM, он может преобразовать класс непосредственно перед тем, как JVM его определит. Мод может переписать игровой метод, внедрить новое поведение или добавить поля, не создавая при этом измененную копию игры на диске. Класс, который в конечном итоге запускается JVM, — это исправленный класс. Чтобы понять, как это вписывается в общую картину, см. Что такое загрузчик модов для Minecraft?.
Часто задаваемые вопросы
При первом использовании, а не при запуске. В первый раз, когда ваш код обращается к типу, JVM запрашивает у загрузчика классов найти и определить его. Это называется ленивой загрузкой.
Загрузчик просит своего родителя загрузить класс, прежде чем пытаться загрузить его самостоятельно. Цепочка родительских загрузчиков поднимается до загрузчика инициализации, который предотвращает повторную загрузку основных типов Java с одним и тем же именем.
Да, если их определяют разные загрузчики классов. JVM идентифицирует класс по его имени и загрузчику, который его определил, поэтому классы с одинаковым именем из разных загрузчиков являются различными типами.
Чтобы видеть игровые классы во время их загрузки и изменять их в памяти. Собственный загрузчик может трансформировать байт-код до того, как JVM определит класс, что позволяет модам изменять игру без редактирования файлов на диске.