Конфигурация версии 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>
Другие вопросы по тегам