Сумасшедший вопрос ClassLoader

Классы:

public interface Inter {
  ...some methods...
}

public class Impl implements Inter {
  ...some implementations...
}

Проблема в том, что по какой-то причудливой причине мне приходится загружать интерфейс Inter с дочерним ClassLoader и классом реализации Impl с родительским ClassLoader.

В этом случае я получу NoClassDefError, потому что родительский ClassLoader, который пытается загрузить реализацию Impl не знает об интерфейсе Inter который был загружен в дочерний ClassLoader.

Есть ли способ загрузки реализации с дочерним ClassLoader (контекст ClassLoader)? Или, может быть, мне нужно написать какой-нибудь собственный ClassLoader для загрузки их обоих (нарушив правило делегирования)?

2 ответа

Решение

Проблема в том, что по какой-то причудливой причине я должен загрузить интерфейс Inter с дочерним ClassLoader и класс реализации Impl с родительским ClassLoader.

Я не могу понять, почему дочерний загрузчик классов должен загружать интерфейс, оставляя родительский загрузчик классов для загрузки реализации. Это должно вызывать проблемы, поскольку в механизме загрузки классов, который используется JVM, нет механизма, чтобы откладывать загрузку классов до дочернего загрузчика классов. Обычный механизм реализации поведения загрузки классов в JVM определен в документации API класса ClassLoader:

Класс ClassLoader использует модель делегирования для поиска классов и ресурсов. Каждый экземпляр ClassLoader имеет связанный родительский загрузчик классов. При запросе на поиск класса или ресурса экземпляр ClassLoader делегирует поиск класса или ресурса своему загрузчику родительского класса, прежде чем пытаться найти сам класс или ресурс. Встроенный загрузчик классов виртуальной машины, называемый загрузчиком классов начальной загрузки, сам по себе не имеет родителя, но может служить родителем экземпляра ClassLoader.

Можно написать собственный загрузчик классов, расширив класс ClassLoader и переопределив метод loadClass(). Расширение этого метода позволяет вам изменить делегирование загрузки класса одним из двух способов:

  • Parent-first: получите родительский загрузчик классов, чтобы загрузить класс первым. Обычно это транзитивное поведение - большинство родительских загрузчиков классов будут откладывать загрузку на своих родителей и т. Д. До тех пор, пока загрузчик загрузчиков классов (корень) не будет достигнут в иерархии загрузчиков классов. Если родительский загрузчик классов не может загрузить класс, дочерний элемент пытается загрузить его. Возможный сбой при загрузке класса должен привести к возникновению исключения ClassNotFoundException.
  • Parent-last: Пользовательский загрузчик классов сначала пытается загрузить класс, прежде чем делегировать его родителю. Родительские загрузчики классов используются только тогда, когда дочерний процесс не может загрузить класс.

Большинство загрузчиков классов реализованы как первоклассные загрузчики классов. Это связано с тем, что механизм делегирования может перемещаться по дереву в направлении вверх, но не в направлении вниз.

Если вы действительно хотите делегировать загрузку и поиск классов дочерним загрузчикам классов в иерархии, вам придется управлять ссылками на них в пользовательском загрузчике классов для родителя. Это непросто, и обычно это вообще не делается, за исключением очень исключительных обстоятельств, поскольку очень легко получить страшные ClassNotFoundException и NoClassDefFoundError, так как нужно быть осторожным при загрузке только необходимых классов из дочерних загрузчиков классов, в то время как остальные должны всегда откладывать на родителя (если я не ошибаюсь, функция разделяемых библиотек в некоторых контейнерах Java EE реализована таким образом).

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

PS: не забудьте использовать AccessController.doPrivileged при загрузке и определении классов.

Вы переопределяете loadClass(имя строки, логическое разрешение) java.lang.ClassLoader?

Я гость, что ты сделал.

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

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