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

Классы:

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

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

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

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

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


person m_vitaly    schedule 18.08.2010    source источник
comment
Это просто не сработает. Вы просто не можете загрузить класс, не имея возможности разрешать статические зависимости этого класса от этого загрузчика классов.   -  person skaffman    schedule 18.08.2010
comment
Как насчет моих последних вопросов о пользовательском ClassLoader?   -  person m_vitaly    schedule 18.08.2010
comment
Нарушение загрузчика классов — плохая идея, на самом деле SecurityManager можно настроить на отключение пользовательских загрузчиков классов, что вызовет хаос при интеграции с другими приложениями. Подумайте о том, что я предоставлю свою собственную реализацию java.lang.String, которая загружается в дочерний загрузчик классов, поскольку контракт делегирования больше не применяется. Это может помочь очистить гадость, если вы можете объяснить причудливую причину   -  person questzen    schedule 18.08.2010
comment
Вы можете реализовать свой собственный загрузчик классов для загрузки обоих из них, но тогда я не вижу преимущества перед использованием обычного загрузчика классов для загрузки обоих из них...   -  person Joeri Hendrickx    schedule 18.08.2010
comment
В чем причудливая причина? Может быть другое решение, кроме как делать что-то странное с загрузчиками классов.   -  person Adam Crume    schedule 18.08.2010
comment
Удалось изменить дизайн, чтобы не было такого странного требования.   -  person m_vitaly    schedule 01.09.2010


Ответы (2)


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

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

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

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

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

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

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

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

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

person Vineet Reynolds    schedule 18.08.2010

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

Я гость вы сделали.

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

person Mercy    schedule 18.08.2010
comment
Я не писал свой собственный загрузчик классов. Я использую tomcat, Impl загружается с помощью обычного ClassLoader, а интерфейс загружается с помощью определенного ClassLoader веб-приложения. - person m_vitaly; 18.08.2010
comment
Виталий, это неправильно. Если вы загружаете реализацию самостоятельно, вам следует попытаться получить доступ к загрузчику классов веб-приложения, чтобы сделать это. В противном случае, если реализация каким-то образом автоматически загружается вашим контейнером, вы должны разместить интерфейс прямо рядом с ней. - person Stroboskop; 18.08.2010