Можно ли загрузить класс без загрузки ссылочных классов / импорта?

(Да, это хакерская и, вероятно, не лучшая практика, но это наименее громоздкое решение)

У меня есть проект, который включает в себя несколько jar-файлов - запускаемый лаунчер, сервер, оболочку для сервера и плагины для сервера.

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

Оболочка использует URLClassLoader для загрузки JAR-сервера и запуска его, что прекрасно работает.

Перед запуском сервера оболочка ищет плагины, которые содержат определенные пути / файлы классов, обычно используемые на сервере, и загружает их, исправляя обычную версию класса.

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

Мне нужно либо запретить загрузчику классов разрешать импорт, либо загрузить классы после сервера и заменить ими уже загруженные классы. Насколько я знаю, второй вариант невозможен без нестабильности и манипулирования байт-кодом?

1 ответ

Прежде всего, отвечая на ваш заглавный вопрос:

Можно ли загрузить класс без загрузки ссылочных классов / импорта?

Ну, вы можете решить не разрешать класс, передав false loadClass, Подробности о том, что влечет за собой класс, можно найти здесь. Затем вы можете сделать разрешение в отдельном шаге, позвонив resolveClass в явном виде.


Однако, если это не дает вам того, чего вы хотите, я не знаю, является ли это единственно возможным решением, и это, безусловно, ужасно (и были определенно лучшие способы подойти к этому с самого начала), но что, если вы это сделаете что-то вроде этого:

Я собираюсь назвать ваши классы, которые служат патчами и должны быть загружены до того, как сервер "предварительно загрузит" классы / код. Я собираюсь вызвать компоненты ваших патчей, которые имеют зависимости от сервера, но чья загрузка должна быть отложена до тех пор, пока сервер не загрузит классы / код "после загрузки".

Для каждого из ваших плагинов:

  1. Удалите все вещи после загрузки из класса патча и переместите его в другой класс, например PluginImplementation или что-то. Затем сделайте экземпляр этого класса реализации членом вашего класса плагина, делегируя ему все необходимые функции-члены, но не создавайте экземпляр PluginImplementation сразу же, и сделать поле члена иметь тип Object (это часто описывается как непрозрачный указатель / шаблон pimpl). По сути, вы рефакторинг, чтобы использовать идиому pimpl, где ваши вещи перед загрузкой кодируются непосредственно, а ваши вещи после загрузки фактически делегируются другому классу, скрытому за Object и не инициализируется сразу. Ваша цель - удалить все зависимости от серверных классов из самого класса плагина, изменив его на минимальный необходимый для загрузки патча, но переместив все мясо в класс реализации, в конечном счете скрытый за непрозрачным указателем.

  2. Теперь загрузите все ваши плагины как обычно. Они должны загружаться сейчас, и, поскольку все компоненты после загрузки, которые имеют зависимости от сервера, были удалены из них, классы сервера не будут загружены.

  3. Теперь ваши плагины выставляют какие-то serverLoaded(), или же initializePlugin() метод или что-то в этом роде. После загрузки класса сервера пройдите и вызовите их на каждом загруженном плагине.

  4. Наконец, в методе инициализации из предыдущего шага ваш плагин создает экземпляры классов после загрузки, используя Class.forName().newInstance(),

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

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

Я уверен, что могут быть лучшие варианты, но я думаю, что это тот, который предполагает наименьший объем работы с учетом того, что у вас уже есть. Вам придется перемещать много кода вокруг; IDE, такая как Eclipse или что-то еще, может, по крайней мере, облегчить выплевывание делегатов (просто временно сделайте поле участника PluginImplementation чтобы помочь IDE, тогда вы можете изменить его обратно на Object после того, как вы сгенерируете весь код), но это должно минимизировать * фундаментальные * изменения в вашей существующей архитектуре.


Вот еще одна идея, хотя я не уверен, насколько хорошо она вписывается в ваш текущий код, или если это вообще возможно в вашей ситуации:

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

  1. Объявите ваш сервер как интерфейс.
  2. Заставьте реализацию вашего конкретного сервера реализовать этот интерфейс.
  3. Измените все плагины для работы на этом интерфейсе, а не на экземпляре самого класса сервера.
  4. Убедитесь, что ваш серверный класс не объявляет какие-либо внутренние классы, которые могут использовать плагины: разбейте любые внутренние классы на верхний уровень (IDE, как Eclipse, может сделать это за вас).

Теперь вам нужно найти способ сообщить каждому плагину экземпляр сервера, с которым он работает, но плагин будет хранить его в элементе с типом базового интерфейса.

Таким образом, загрузка плагинов не будет загружать сам сервер, только базовый интерфейс.

Я думаю, что эта идея чертовски проста и менее хакерская, чем описанная выше, я просто не знаю, является ли она более или менее осуществимой, чем описанная выше.


Обратите внимание, что оба эти варианта не обязательно гарантируют успех, но на практике они, вероятно, будут работать.

Другие вопросы по тегам