Конфигурация версии Java Maven игнорируется Eclipse/Idea
Я имею:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
Тем не менее у меня нет проблем с заявлением:
public enum DirectoryWatchService {
INSTANCE;
private java.util.Optional<String> test;
private java.nio.file.Files files;
}
Затмение не беспокоит. IntelliJ тоже. Даже Maven не беспокоит. Я даже могу сделать чистую посылку. Строит чертову вещь без какого-либо предупреждения.
1 ответ
Вы попадаете в непонимание кросс-компиляции source
/ target
опции. Использование основной версии в вашем classpath (JDK 7 или 8), но вы хотите скомпилировать против вспомогательной версии (6 в вашем случае).
Компиляция будет в порядке, но вы будете иметь ошибки во время выполнения (т.е. NoClassDefFoundError
или же NoSuchMethodError
вероятно, также более общий LinkageError
).
С помощью source
/ target
Java-компилятор можно использовать в качестве кросс-компилятора для создания файлов классов, запускаемых на JDK, реализующих более раннюю версию спецификации Java SE.
Распространено мнение, что двух вариантов компилятора будет достаточно. Тем не менее source
Опция указывает, с какой версией мы компилируем target
опция определяет самую низкую версию Java для поддержки.
Компилятор работает над генерацией байт-кода и source
а также target
используются для генерации совместимого байт-кода во время кросс-компиляции. Однако Java API не обрабатываются компилятором (они предоставляются как часть установки JDK, известного rt.jar
файл). Компилятор не знает API, он просто компилируется с текущим rt.jar
, Следовательно, при компиляции с target=1.6
используя JDK 1.7, компилятор все равно будет указывать на JDK 7 rt.jar
,
Итак, как мы можем получить правильную кросс-компиляцию?
Начиная с JDK 7, Javac печатает предупреждение в случае source
/ target
не в сочетании с bootclasspath
вариант. bootclasspath
опция является ключевой опцией в этих случаях, чтобы указать на rt.jar
желаемой целевой версии Java (следовательно, вам нужно установить целевой JDK на вашем компьютере). В качестве таких, javac
будет эффективно компилироваться против хорошего Java API.
Но этого все еще может быть недостаточно!
Не все Java API происходит от rt.jar
, Другие классы предоставляются lib\ext
папка. Дальше javac
для этого используется опция, extdirs
, Из официальных документов Oracle
Если вы выполняете кросс-компиляцию (компилирование классов с использованием начальной загрузки и классов расширения другой реализации платформы Java), этот параметр указывает каталоги, содержащие классы расширения.
Следовательно, даже используя source
/ target
а также bootclasspath
Опции, мы можем все еще что-то пропустить во время кросс-компиляции, как также объяснено в официальном примере, который поставляется с документацией javac.
Java-платформа JDAC также по умолчанию компилируется с собственными классами начальной загрузки, поэтому нам нужно указать javac, чтобы она компилировалась с классами начальной загрузки JDK 1.5. Мы делаем это с -bootclasspath и -extdirs. Невыполнение этого требования может привести к компиляции с использованием API Java Platform, который не будет присутствовать на виртуальной машине 1,5 и будет давать сбой во время выполнения.
Но.. этого все еще может быть недостаточно!
Из официальной документации Oracle
Даже когда все параметры bootclasspath и -source/-target установлены соответствующим образом для кросс-компиляции, внутренние контракты компилятора, такие как компиляция анонимных внутренних классов, могут различаться, например, между javac в JDK 1.4.2 и javac в JDK 6 работает с опцией -target 1.4.
Предлагаемое решение (из официальной документации Oracle)
Самый надежный способ создания файлов классов, которые будут работать с конкретным JDK и более поздними версиями, - это скомпилировать исходные файлы, используя самый старый интересующий JDK. За исключением этого, загрузочный путь должен быть установлен для надежной кросс-компиляции в более старый JDK.
Значит, это действительно невозможно?
Spring 4 в настоящее время поддерживает Java 6, 7 и 8. Даже с использованием функций и API Java 7 и Java 8. Как тогда он может быть совместим с Java 7 и 8?!
Весна использует source
/ target
а также bootclasspath
гибкость. Spring 4 всегда компилируется с source
/ target
в Java 6, так что байт-код все еще может работать под JRE 6. Следовательно, никакие функции языка Java 7/8 не используются: синтаксис поддерживает уровень Java 6.
Но он также использует Java 7 и Java 8 API! Следовательно, bootclasspath
опция не используется. Необязательно, Stream и многие другие Java 8 API используются. Затем он вводит bean-компоненты в зависимости от Java 7 или Java 8 API, только когда JRE 7/8 обнаруживается во время выполнения: разумный подход!
Но как же тогда Spring обеспечивает совместимость API?
Использование плагина Maven Animal Sniffer.
Этот плагин проверяет, является ли ваше приложение API-совместимым с указанной версией Java. Называется сниффером животных, потому что Sun традиционно называет разные версии Java в честь разных животных (Java 4 = Мерлин (птица), Java 5 = Тигр, Java 6 = Мустанг (лошадь), Java 7 = Дельфин, Java 8 = нет животного).
Вы можете добавить следующее в ваш файл POM:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.14</version>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java16</artifactId>
<version>1.0</version>
</signature>
</configuration>
<executions>
<execution>
<id>ensure-java-1.6-class-library</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
И сборка завершится неудачно, как только вы будете использовать JDK 7 API, когда вы захотите использовать только JDK 6 API.
Это использование также рекомендуется в официальном source
/ target
страница maven-compiler-plugin
Примечание: просто настройка
target
Опция не гарантирует, что ваш код действительно выполняется на JRE с указанной версией. Подводный камень - непреднамеренное использование API, которые существуют только в более поздних JRE, что может привести к сбою кода во время выполнения с ошибкой связывания. Чтобы избежать этой проблемы, вы можете либо сконфигурировать путь к классу загрузки компилятора в соответствии с целевым JRE, либо использовать плагин Animal Sniffer Maven, чтобы убедиться, что ваш код не использует непреднамеренные API.
Обновление Java 9
В Java 9 этот механизм был радикально изменен на следующий подход:
javac --release N ...
Будет семантически эквивалентно
javac -source N -target N -bootclasspath rtN.jar ...
Информация об API более ранних выпусков доступна для
javac
- Хранится в сжатом виде
- Предоставляйте только Java SE N и JDK N -экспортированные API, которые не зависят от платформы
- Тот же набор значений деблокирования N, что и для
-source
/-target
- Несовместимые комбинации опций отклонены
Основные преимущества --release N
подход:
- Пользователю не нужно управлять артефактами, хранящими старую информацию API
- Следует удалить необходимо использовать такие инструменты, как плагин Maven Animal Sniffer
Может использовать более новые идиомы компиляции, чем
javac
в старых версиях
- Исправление ошибок
- Улучшения скорости
Обновление на Java 9 и Maven
С версии 3.6.0
, maven-compiler-plugin
обеспечивает поддержку Java 9 через его release
опция:
-release
аргумент для компилятора Java, поддерживается с Java9
Пример:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<release>8</release>
</configuration>
</plugin>