Проверьте обратную совместимость на уровне API библиотеки, созданной с использованием более нового JDK - PullRequest
0 голосов
/ 30 сентября 2019

Недавно товарищ по команде использовал следующую функцию в нашем коде Java 8: Matcher.replaceAll (Функция заменителя) . Функция была представлена ​​в Java 9, но поскольку он использует более новый компилятор, API-функция была просто найдена в файле rt.jar JDK, и никто не заметил, что это не будет работать в реальных средах Java 8. Параметры совместимости установлены правильно, и подпроект gradle имеет следующие параметры:

sourceCompatibility = 1.8
targetCompatibility = 1.8

У меня были очень похожие проблемы в то время, когда я впервые использовал функцию Java 6 String.isEmpty в коде Java 5 -код попал в релиз и рухнул там.

Что я могу сделать, чтобы обеспечить использование правильного API. Поскольку это общая библиотека, нужно ли мне использовать (и устанавливать, поддерживать ...) другой JDK для этого подпроекта gradle, или существует какой-то сканер совместимости, который запускает встроенный jar и проверяет все ссылки rt?

Ответы [ 2 ]

2 голосов
/ 30 сентября 2019

Как вы заметили, две конфигурации совместимости не учитывают API более старых версий - только синтаксис, семантику и полученный байт-код.

Существует два варианта, которые вы можете выбрать. Один из них - установить на компьютер JDK 8 и настроить Gradle для его использования при компиляции проекта. Выглядит это так:

tasks.withType(JavaCompile) {
    options.fork = true
    options.forkOptions.executable = "$java8Home/bin/javac"
    options.bootstrapClasspath = files("$java8Home/jre/lib/rt.jar")
}

Недостатком здесь является то, что вам сначала потребуется установить JDK 8, и, поскольку он, вероятно, будет установлен в разных местах, вам, вероятно, потребуется настроитьэто с переменной или свойством среды (я назвал это java8Home здесь).

Однако, начиная с Java 9, JDK теперь знает о документированных API предыдущих версий, и вы можете выбрать, какой из нихиспользовать с новым флагом --release. Это не сработает, если вы используете недокументированные API, но это означает, что вы можете скомпилировать свой проект с любыми версиями Java и при этом сделать полученные классы совместимыми с Java 8. Вы можете сделать это следующим образом:

tasks.withType(JavaCompile) {
    if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
        options.compilerArgs.addAll(['--release', '8'])
    }
}

Обратите внимание, что оператор if существует только в том случае, если вам все еще нужно поддерживать запуск Gradle с Java 8 (через переменную JAVA_HOME). Если вы используете только более поздние версии, его можно удалить, поэтому вы всегда должны устанавливать 'compilerArgs'.

1 голос
/ 30 сентября 2019

В некоторых версиях Java возможно построить код Java на более новом JDK для запуска на более старом JDK / JRE. Вы уже обнаружили опции --source и --target для javac и соответствующие настройки Gradle. Другая вещь, которую вы можете сделать, это использовать --bootclasspath, чтобы сказать javac для компиляции с библиотеками времени выполнения для более старой версии Java.

Поскольку вы используете Gradle, посмотрите "gradle-java-cross"-compile-plugin "(https://github.com/nebula-plugins/gradle-java-cross-compile-plugin). Я не могу найти документацию для него, но он, очевидно, имеет дело с --target и --bootclasspath.


Сказав это, я нене думаю, что кросс-компиляция Java является хорошим решением.

Я бы порекомендовал вам настроить сервер непрерывной интеграции (CI) (например, Jenkins) с установками JDK для всех интересующих вас версий Java. затем настройте задания для сборки кода и , запустите свои модульные тесты для каждой версии Java.

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

...