Как использовать два несравнимых класса с одинаковым полным именем?

#java #spring #classloader

#java #spring #classloader

Вопрос:

Проблема: Мое приложение использует две библиотеки, которые используют несовместимые версии третьей библиотеки. Кто-нибудь знает какой-нибудь метод для изоляции классов?

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

Я также думаю о Spring, но не знаю, обеспечивает ли он такую изоляцию.

Комментарии:

1. Я так понимаю, эти два класса не находятся в отдельных пространствах имен?

2. Я не понимаю, что означает «пространство имен» в контексте Java. Имя пакета? Пусть они будут в разных пакетах, если это важно.

3. Извините, да, я имел в виду посылки. И еще, я неправильно истолковал ваш вопрос, поэтому не думаю, что это актуально!

4. Сообщали ли вы об этой проблеме авторам библиотеки, которая использует старую версию? Возможно, стоит это сделать. Это действительно неприятная ситуация.

Ответ №1:

Загрузчики классов — это, по сути, элементы, которые придают значение классам в JVM. Они образуют иерархию, корень которой находится в JVM и загружает классы Java. ApplicationClassLoader — это первый ClassLoader, который вы должны рассмотреть, поскольку он загружает все классы вашего приложения.

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

Два класса могут быть изолированы, если они находятся в 2 разных classloader, а не в app classloader. Это не сложно сделать. Вам нужно только создать classloader (например, URLClassLoader), указав его родительский элемент и его classpath (место, где находится байт-код)

затем вы говорите ему, чтобы он загрузил класс. Он запросит своего родителя, и если класс еще не загружен, он выполнит поиск по его пути к классу и загрузит его. Если вы создадите другой classloader, прикрепленный к тому же родительскому классу, классы, загруженные первым, никогда не будут видны второму классу, поскольку они являются братьями и сестрами. И второй может загружать класс с таким же именем без каких-либо проблем

Это довольно хорошая изоляция

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

Ответ №2:

если мы загрузим одну версию класса, мы не сможем загрузить другую — класс уже загружен?

Неверно. Загрузчик класса считается частью идентификатора класса. Если он загружен другим classloader, он считается другим классом.

Комментарии:

1. Означает ли это, что мы можем обрабатывать классы как объекты Java? Например, преобразовать их в массив или список или создать более одного экземпляра?

2. Классы @Nulldevice могут быть созданы только из байт-кода загрузчиками классов. Однако, да, они являются объектами и могут быть помещены в массивы или списки. Я думаю, что только загрузчики классов должны были бы делать такие вещи.

Ответ №3:

Если хотя бы одна из библиотек с открытым исходным кодом и библиотека, от которой они зависят, с открытым исходным кодом, нет необходимости возиться с загрузчиками классов. Просто массово переименуйте все пакеты в одной из версий библиотеки, от которой зависит, убедившись, что также изменен весь код, который ссылается на эти пакеты. Эй, вуаля — никаких столкновений имен. Это то, что Sun на самом деле сделала для JDK, когда они включили Xerces и Xalan за кулисами.

Комментарии:

1. Что, если я скажу, что это проект, разработанный ~ 50 инженерами-программистами? Я не хочу нести ответственность за исправление ошибок в тысячах строк кода. Не так-то просто изменить версии некоторых библиотек, даже с открытым исходным кодом.

2. Вы можете просто написать скрипт, который выполняет необходимые переименования, и запускать этот скрипт в каждой новой версии, которая выпускается. Следите за Class.forName , но это должно быть относительно просто.

3. Сэр, в нем десятки тысяч строк кода — вы шутите?

4. Нет, я не шучу. Скрипту все равно, со сколькими строками кода ему приходится работать.

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