Java развивается гораздо быстрее, чем раньше. Но не все проекты, над которыми мы работаем, поспевают за этим темпом.

У меня есть проекты на Java 8, 11 и 17, и иногда я хочу поиграть с имеющимися сборками более новых версий.

Как обеспечить их создание без необходимости постоянно переключать среды выполнения Java?

Переключение версий Java

Переключение версий Java в командной строке не должно быть трудным

В моем случае это так же просто, как набрать j8j11j17

Но делать это каждый раз, когда вы видите сообщение «версия 17 не поддерживается», немного утомительно. Что еще более важно, это не решает основную причину проблемы.

Так в чем же причина, спросите вы?

Основная причина здесь в том, что по умолчанию подключаемый модуль компилятора Maven будет использовать компилятор Java, поставляемый со средой выполнения Java, в которой работает Maven.

Вы можете увидеть, какой именно, выполнив команду mvn -version:

$ mvn -version
Apache Maven 4.0.0-alpha-1-SNAPSHOT (9e19b57c720d226b0b30992535819f700a665d14)
Maven home: /usr/local/Cellar/maven-snapshot/4.0.0-alpha-1-SNAPSHOT_117/libexec
Java version: 11.0.10, vendor: AdoptOpenJDK, runtime: /Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home
Default locale: en_GB, platform encoding: UTF-8
OS name: "mac os x", version: "10.15.7", arch: "x86_64", family: "mac"

В этом примере Maven использует Java 11 Development Kit.

Но в моем проекте, чтобы задать для параметра компилятора -release значение 17, я установил соответствующее свойство maven.compiler.release в Maven Compiler Plugin.

Чтобы использовать версии Java ниже 9, вы должны установить два свойства: maven.compiler.source и maven.compiler.target.

Компилятор из Java 11 Development Kit, очевидно, не знает, как компилировать на Java 17, поэтому мы видим сообщение «релиз версии 17 не поддерживается».

Toolchain спешит на помощь!

К счастью, решение есть в нашем распоряжении. На самом деле, руководство «Компиляция исходников с использованием другого JDK» Maven Compiler Plugin начинается с него:

Предпочтительным способом использования другого JDK является использование механизма toolchain.

Так что же такое toolchain? В том же руководстве, несколькими строками позже, резюмируется:

Toolchain это способ централизованно указать путь к JDK, который будет использоваться для всех плагинов, независимо от того, на каком запущен сам Maven.

Последняя часть этого предложения очень важна, поэтому позвольте мне еще раз подчеркнуть: цепочка инструментов не зависит от той, в которой работает сам Maven.

Итак, как мы это используем?

Во-первых, мы используем цель (goal) toolchain Apache Maven Toolchains Plugin, чтобы проверить, могут ли требования цепочки инструментов для проекта быть удовлетворены с помощью настроенных цепочек инструментов:

<project>
  <!-- omitted for brevity -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-toolchains-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
          <toolchains>
            <!-- this project needs a JDK toolchain, version 17 -->
            <jdk>
              <version>17</version>
            </jdk>
          </toolchains>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>toolchain</goal>
            </goals>
            <!-- the toolchain goal binds to the validate phase automatically -->
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Фрагмент выше говорит: мы указываем, что проекту нужна toolchain типа JDK с версией 17. Если мы попытаемся снова собрать проект, сборка все равно не удастся, но сообщение будет другим:

[INFO] --- maven-toolchains-plugin:3.0.0:toolchain (default) @ sample-project ---
[INFO] Required toolchain: jdk [ version='17' ]
[ERROR] No toolchain found for type jdk
[ERROR] Cannot find matching toolchain definitions for the following toolchain types:
jdk [ version='17' ]

Это ясное сообщение: Maven не может собрать этот проект, так как не установлен JDK toolchain версии 17. Что ж, он есть, но мы не сказали Maven, где его найти.

Определение Toolchain

Мы можем сделать это с помощью Toolchain Configuration, который находится в ~/.m2/toolchains.xml.

Чтобы декларировать JDK 17 toolchain, которая имеется на моей машине, я должен написать:

<?xml version="1.0" encoding="UTF8"?>
<toolchains>
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>17</version>
    </provides>
    <configuration>
      <jdkHome>/Library/Java/JavaVirtualMachines/adoptopenjdk-17.jdk/Contents/Home</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

Как видите, этот файл содержит полный путь установки Java.

Это делает файл специфичным для машины, на которой он хранится. Вот почему он находится в каталоге .m2 для локального пользователя и почему этот файл не может быть частью системы управления версиями исходного кода проекта. Каждому, кто работает в команде, потребуется собственная копия файла, адаптированная по мере необходимости для правильных путей. Сюда также входят серверы сборки, на которых будет собираться проект!

Популярность наборов инструментов

Около десяти месяцев назад я опросил в Твиттере, знают ли люди об этой функции и используют ли они ее.

Хотя отклик был не очень большим, интересно взглянуть на результаты:

  1. Примерно половина людей не знает о существовании Toolchain.

  2. Примерно треть людей, знакомых с Toolchain, не используют его.

Как это может быть? Toolchain похожа на мощную функцию. Даже те, кто знаком с Toolchains, не всегда им пользуются.

Я думаю, частично это объясняется тем, что сам Maven написан на Java.

Представьте, если бы Maven был написан на другом языке. В этом случае вам всегда нужно было бы указывать, где найти Java Development Kit, поскольку Maven не знал бы об этом автоматически. 

Но теперь, когда Maven работает на JVM, он уже знает одну JVM, которую может использовать. Возможно, это не лучший вариант для текущего проекта, но, по крайней мере, он есть, и он попытается его использовать.

Что дальше?

Мы уже видели, что подключаемый модуль компилятора Maven понимает концепцию цепочек инструментов (Toolchain) и знает, как ее использовать.

Но это не единственный плагин, который может принести пользу.

Действительно, многие официальные плагины Maven понимают эту концепцию и используют цепочку инструментов при настройке. Сюда входят плагины Javadoc, JAR и Surefire, и это лишь некоторые из них.

Даже некоторые неофициальные плагины работают с цепочками инструментов, например, Protocol Buffer и плагин Keytool. Полный список находится в руководстве по использованию Toolchains.

Помимо набора инструментов JDK, можно даже декларировать собственные цепочки инструментов.

Это выходит за рамки данной статьи, но если вам интересно, вы можете начать с документации по плагину Toolchain.

Комментарии (7)


  1. upagge
    26.01.2022 11:24

    Я тоже не слышал об этой штуке, но если честно у меня и такой проблемы нет. У меня тоже есть проекты на 8,11 и 17 джавах, но Idea отлично разруливает какой jdk нужно использовать.


    1. LeshaRB
      26.01.2022 11:35

      Мавен же с дефолтной работает


      1. Anthrax_Beta
        27.01.2022 09:02

        можно перед запуском мавена выставить переменную окружения JAVA_HOME


        1. LeshaRB
          27.01.2022 12:47

          Ну так задача была не в том что выставлять каждый раз

          А чтоб в зависимости от проекта (какая Java используется) такая же использовалась и в мавен


  1. dimakiev
    26.01.2022 15:05
    +4

    SDKMan отлично справляется. и стал уже привычным.
    попробую toolchain, конечно. пока не попробуешь - не поймешь, что удобнее.
    спасибо


    1. Filex
      27.01.2022 10:30
      +1

      Отпишитесь потом, что для вас оказалось удобнее.


  1. odisseylm
    27.01.2022 13:52

    Важно помнить, что это хороший способ только для компиляции. Если же в вашей компании используются кастомные maven плагины (которые не работают со слишком новыми/старыми версиями jdk), то придётся использовать, что-то типа ваших j8, j11 (у нас это были mvn8, mvn11, ...)