Использование разных версий зависимостей в отдельных модулях платформы 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