Использование разных версий зависимостей в отдельных модулях платформы Java

Я ожидал, что можно использовать, например, Guava-19 в myModuleA и guava-20 в myModuleB, поскольку у модулей головоломки есть свой собственный путь к классу .

Допустим, myModuleA использует Iterators.emptyIterator(); - который удален в guava-20, а myModuleB использует новый статический метод FluentIterable.of(); - который не был доступен в Гуава-19. К сожалению, мой тест отрицательный. Во время компиляции это выглядит хорошо. В отличие от времени выполнения, результатом является NoSuchMethodError. Это означает, что класс, который был первым в загрузчике классов, решает, какой из них потерпит неудачу.

Инкапсуляция с базовой связью? Я нашел причину для себя. Это не может быть поддержано, потому что транзитивные зависимости будут иметь ту же проблему, что и раньше. Если класс guava, имеющий конфликт версий, произошел в сигнатуре в ModuleA и ModuleB, это зависит от него. Какой класс следует использовать?

Но почему по всему интернету мы можем прочитать "мозаику - система модулей останавливает ад пути к классам"? Теперь у нас есть несколько меньших "похожих на класс" путей с одинаковыми проблемами. Это скорее неопределенность, чем вопрос.

3 ответа

Решение

Конфликты версий

Сначала исправление: вы говорите, что у модулей есть свой собственный путь к классу, что неверно. Путь к классу приложения остается без изменений. Параллельно с этим был введен путь к модулю, но он по сути работает таким же образом. В частности, все классы приложений загружаются одним и тем же загрузчиком классов (по крайней мере, по умолчанию).

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

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

Модуль Ад?

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

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

Теоретически в вашем приложении можно использовать разные версии одной и той же библиотеки. Концепция, которая позволяет это: наслоение!

Когда вы изучаете Jigsaw под капотом, вы обнаружите целый раздел, посвященный этой теме.

Основная идея заключается в том, что вы можете дополнительно группировать модули, используя эти слои. Слои строятся во время выполнения; и у них есть свой собственный загрузчик классов. Смысл: должно быть абсолютно возможно использовать модули в разных версиях в одном приложении - им просто нужно перейти на разные уровни. И как показано - этот вид "поддержки нескольких версий" активно обсуждается людьми, работающими над java/jigsaw. Это не скрытая функция - она предназначена для поддержки разных версий модулей под одной крышей.

Единственный отказ от ответственности на данный момент: к сожалению, нет никаких "полных" примеров исходного кода (о которых я знаю), поэтому я могу только ссылаться на эту презентацию Oracle.

Другими словами: на горизонте есть какое- то решение этой проблемы управления версиями, но потребуется больше времени, чтобы получить опыт работы с реальным кодом с этой новой идеей. А если быть точным: у вас могут быть разные слои, которые изолируются разными загрузчиками классов. Не существует поддержки, которая позволила бы вам использовать один и тот же объект как modV1 и modV2 одновременно. Вы можете иметь только два объекта, один из которых использует modV1, а другой - modV2.

(Немецкие читатели могут захотеть взглянуть сюда - эта публикация содержит еще одно введение в тему слоев).

Java 9 не решает такие проблемы. Вкратце, то, что было сделано в java 9, - это расширение классических модификаторов доступа (public, protected, package-private, private) до уровней jar.

До java 9, если модуль A зависит от модуля B, тогда все открытые классы из B будут видны для A.

В Java 9 видимость может быть настроена, поэтому она может быть ограничена только подмножеством классов, каждый модуль может определять, какие пакеты экспортировать и какие пакеты требуются.

Большая часть этих проверок выполняется компилятором.

Из времени выполнения perspective(classloader architecture)больших изменений не происходит, все модули приложения загружаются одним и тем же загрузчиком классов, поэтому невозможно иметь один и тот же класс с разными версиями в одном и том же jvm, если вы не используете модульную структуру, такую ​​как OSGI, или сами не управляете загрузчиками классов.

Как уже намекали другие, в этом могут помочь слои JPMS. Вы можете использовать их просто вручную, но вам может быть полезен Layrry, который представляет собой беглый API и средство запуска на основе конфигурации для запуска многоуровневых приложений. Он позволяет вам определять структуру слоев с помощью конфигурации и запускает для вас график слоев. Он также поддерживает динамическое добавление / удаление слоев во время выполнения.

Отказ от ответственности: я первый создатель Layrry

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