Java 9 + Maven: что произойдет, если две зависимости экспортируют один и тот же модуль?

Я получаю java.lang.NoSuchMethodError при попытке вызвать метод Java. Насколько я могу судить, путь к классам идентичен во время компиляции и времени выполнения, поэтому этой ошибки возникать не должно.

Шаги воспроизведения:

  1. Создайте 4 проекта Maven:

    • MyLibrary
    • ExtensionPresent
    • Расширение отсутствует
    • UserCode
  2. Модули ExtensionPresent и ExtensionMissing экспортируют одно и то же имя модуля.

  3. ExtensionMissing экспорт:

package dummy;

public class Extension {}
  1. ExtensionPresent экспорт:
package dummy;

public class Extension {

    public static void present() {
        System.out.println("Extension present!");
    }
}
  1. MyLibrary объявляет ExtensionMissing как зависимость.
  2. UserCode объявляет MyLibrary как зависимость.
  3. UserCode.main() вызывает Extension.present(). Это вызывает ошибку времени компиляции, потому что ExtensionMissing не содержит этого метода.
  4. Теперь самое интересное ... В проекте UserCode добавьте ExtensionPresent в качестве зависимости после MyLibrary.
  5. Я больше не получаю ошибку компилятора (теперь метод присутствует во время компиляции).
  6. Когда я пытаюсь вызвать UserCode.main(), я получаю:
--- exec-maven-plugin:1.6.0:exec (default-cli) @ mavenproject3 ---
Exception in thread "main" java.lang.NoSuchMethodError: dummy.Extension.present()V

Это ошибка конфигурации моего проекта, реализации Maven или инструментов JDK?

(Кстати, я делаю это в попытке решить: Реализация архитектуры плагина (во время компиляции) без разделенных пакетов < / а>)

ОБНОВЛЕНИЕ: это исполняемый тестовый пример: https://github.com/cowwoc/exec-maven-plugin-class-shadowing


person Gili    schedule 05.12.2017    source источник
comment
экспортировать модуль с одним и тем же именем. вы случайно имеете в виду экспорт пакетов с одинаковым именем? или у них одинаковое название модуля? Я предполагаю, что использование exports противоречит соглашению Java9 в вопросе.   -  person Naman    schedule 05.12.2017
comment
@nullpointer То же имя модуля, то же имя пакета, то же имя класса. Единственная разница - наличие или отсутствие метода.   -  person Gili    schedule 05.12.2017
comment
Каковы ваши возможные команды javac (во время компиляции) и java (во время выполнения), не могли бы вы обновить их вопрос, пожалуйста? Просто чтобы убедиться, что это не дубликат Java 9: ​​в пути к модулю может быть 2 модуля с одинаковыми именами   -  person Naman    schedule 05.12.2017
comment
@nullpointer Я обновил вопрос, указав запрошенную вами информацию.   -  person Gili    schedule 05.12.2017
comment
Вы можете попробовать запустить mvn dependency:tree, чтобы получить эффективный список используемых зависимостей. Кроме того, отличается ли ваше поведение при вызове UserCode из командной строки java -jar ...?   -  person scrutari    schedule 05.12.2017
comment
@scrutari mvn dependency:tree возвращает ожидаемый результат. Запуск проекта с использованием java -cp ... отлично работает, если я поменял местами зависимости ExtensionPresent, ExtensionMissing.   -  person Gili    schedule 06.12.2017
comment
@Gili Глядя на эту команду ... Не является ли использование пути к классам во время компиляции и только пути к модулю во время выполнения возможной причиной разницы в поведении?   -  person Naman    schedule 06.12.2017
comment
@nullpointer Я не думаю, что в данном случае это будет иметь значение. Что еще более важно, обратите внимание, как порядок ExtensionPresent и ExtensionMissing меняется между компилятором и плагином exec. Плагин компилятора делает это правильно. Плагин exec перечисляет их в неправильном порядке. Если я вызываю java вручную с замененными зависимостями, программа работает нормально. Я только что обновил вопрос ссылкой на исполняемый тестовый пример. Пожалуйста, попробуйте.   -  person Gili    schedule 06.12.2017
comment
Просто для информации, выполнение mvn clean install в вашем клонированном тестовом примере также не выполняется. Ошибка компиляции [ERROR] ../exec-maven-plugin-class-shadowing/UserCode/src/main/java/testcase/Main.java:[8,18] cannot find symbol [ERROR] symbol: method present() [ERROR] location: class extension.Extension   -  person Naman    schedule 06.12.2017
comment
@nullpointer Странно, здесь нет. Я использую Maven 3.5.2, Oracle JDK 9.0.1. А ты?   -  person Gili    schedule 06.12.2017
comment
@Gili Те же версии. Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T13: 28: 13 + 05: 30) Домашняя страница Maven: /usr/local/Cellar/maven/3.5.2/libexec Версия Java: 9.0.1, поставщик : Oracle Corporation Домашняя страница Java: /Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home Локаль по умолчанию: en_US, кодировка платформы: UTF-8 Имя ОС: mac os x, версия: 10.10.5, архитектура: x86_64, семейство: mac   -  person Naman    schedule 06.12.2017
comment
@nullpointer Это дико. Я могу воспроизвести ошибку, о которой вы упомянули, но если я перенесу папку проекта в другой каталог, сборка заработает. Это также на 100% воспроизводимо. Не могли бы вы снова вытащить репозиторий и сказать мне, в чем разница между good.txt и bad.txt? Это журналы сборки в двух разных каталогах. Я вижу, что maven-compiler-plugin использует неправильный порядок зависимостей в bad.txt, но я не могу понять, почему изменение каталога привело бы к этому.   -  person Gili    schedule 06.12.2017
comment
Не очень уверен в разнице на вашей машине. Я могу воспроизвести сбой в любом месте своего каталога.   -  person Naman    schedule 06.12.2017
comment
@nullpointer Я не могу понять, почему maven-compiler-plugin поступает неправильно во всех каталогах, кроме упомянутого в good.txt, но независимо ... Согласно stackoverflow.com/a/793193/14731 все плагины Maven должны учитывать порядок путей к классам, как указано в pom.xml. Они не. Звучит как ошибка, не так ли?   -  person Gili    schedule 06.12.2017
comment
Не очень уверен в сохранении порядка выполнения в текущей версии maven. Но, возможно, это проблема. Поскольку порядок будет иметь значение, когда зависимости разрешаются в пути к модулю, когда у вас есть два модуля с одинаковыми именами на графике.   -  person Naman    schedule 06.12.2017
comment
@nullpointer Значит, я не сумасшедший. Поведение действительно меняется в зависимости от того, в каком каталоге находится проект (из-за использования HashMap). Кроме того, теперь это подтвержденная ошибка. См. issues.apache.org/jira/browse/   -  person Gili    schedule 07.12.2017


Ответы (1)


Когда я включаю ведение журнала отладки, я вижу, что плагины запускают следующие команды:

  • compiler-maven-plugin вызывает: javac -d ~/UserCode/target/classes -classpath ~/UserCode/target/classes: --module-path ~/.m2/repository/ExtensionPresent/1.0-SNAPSHOT/ExtensionPresent-1.0-SNAPSHOT.jar:~/.m2/repository/MyLibrary/1.0-SNAPSHOT/MyLibrary-1.0-SNAPSHOT.jar:~/.m2/repository/ExtensionMissing/1.0-SNAPSHOT/ExtensionMissing-1.0-SNAPSHOT.jar: -sourcepath ~/UserCode/src/main/java:~/UserCode/target/generated-sources/annotations: -s ~/UserCode/target/generated-sources/annotations -g -nowarn -target 9 -source 9 -encoding UTF-8
  • maven-exec-plugin вызывает java, --module-path, ~/UserCode/target/classes:~/.m2/repository/MyLibrary/1.0-SNAPSHOT/MyLibrary-1.0-SNAPSHOT.jar:~/.m2/repository/ExtensionMissing/1.0-SNAPSHOT/ExtensionMissing-1.0-SNAPSHOT.jar:~/.m2/repository/ExtensionPresent/1.0-SNAPSHOT/ExtensionPresent-1.0-SNAPSHOT.jar, -m, UserCode/com.usercode.Main]

Это заставило меня поверить, что maven-exec-plugin перечисляет зависимости в неправильном порядке. Я попытался вызвать приложение вручную, поменять местами зависимости, и, конечно же, программа запустилась.

Я отправил отчет об ошибке для maven-exec-plugin.

Я подал второй отчет об ошибке для maven-compiler-plugin, потому что кажется, что он также передает неправильный порядок javac в зависимости от имени каталога проекта (странно, но верно).

person Gili    schedule 05.12.2017
comment
Основная проблема заключается в том, что в разных записях путей к модулям допускаются дублирующиеся модули, и Maven создает запись для каждой зависимости. Это означает, что мы вернулись к ситуации, когда порядок в командной строке действительно имеет значение (как в аду JAR). - person Nicolai Parlog; 06.12.2017
comment
@Nicolai Order должен иметь значение (это функция, а не ошибка). Есть законные причины для необходимости переопределения ресурсов и классов. См. issues.apache.org/jira/browse/MNG-1412, чтобы узнать, почему другие тоже этого хотели. - person Gili; 06.12.2017
comment
Я не говорю, что это ошибка, просто поведение Maven подрывает надежную конфигурацию (ее отсутствие и является причиной проблемы). Это, вероятно, хороший компромисс, но он все же может иметь неприятные последствия. - person Nicolai Parlog; 06.12.2017
comment
@Nicolai Как поведение Maven подрывает надежную конфигурацию? Сохранение порядка итераций на самом деле улучшает повторяемость построений. - person Gili; 07.12.2017
comment
Зависимость от порядка итераций по своей сути ненадежна (как вы сами убедились), поэтому модульная система пытается избавиться от нее. Он предпочитает хранить все зависимости в одной папке, чтобы проверить их все сразу. Это (а) означает, что он применяет больше проверок (например, на наличие дубликатов; делает конфигурацию более согласованной и, следовательно, более надежной) и (б) что компилятор и JVM с большей вероятностью увидят одну и ту же конфигурацию (что делает ее более воспроизводимой и, следовательно, более надежной) . - person Nicolai Parlog; 07.12.2017
comment
@Nicolai Чепуха. Расширение подстановочных знаков пути к классам и эквивалент пути к модулям (включая все модули в каталоге) - не более чем удобные функции. Авторы JDK по праву предпочли выдать ошибку в случае двусмысленности пути к модулю, а не молча вывести из строя, как это делает механизм пути к классам, но это никоим образом не означает, что они пытаются отказаться от явного упорядочивания. Затенение ресурсов и классов - это исключительно допустимые варианты использования, которые можно надежно реализовать, перечислив отдельные записи модулей. Если вы не читали ниже, это подтвержденная ошибка в Maven. - person Gili; 07.12.2017
comment
Да отказываться от не правильно - поменьше акцентировать было бы лучше. И да, слежка - это законный вариант использования. Но это редкость с хрупким раствором. Трудно спорить с обратным, учитывая причину, по которой вы полагались на него, и тот факт, что он обнаружил ошибку в наиболее популярных Инструмент сборки Java. Кроме того, Роберт Шольте, похоже, разделяет мое мнение о порядке размещения заказов. - person Nicolai Parlog; 07.12.2017
comment
Послушайте, я не говорю, что вы, JPMS или Maven делаете что-то не так - мой аргумент заключается в том, что большинство вариантов использования и надежная конфигурация были бы лучше реализованы, если бы зависимости регулярно подвергались проверкам неоднозначности модульной системы. . - person Nicolai Parlog; 07.12.2017