Всем привет!
Сегодня расскажу о нескольких неочевидных особенностях работы Maven.
1) за каждый этап жизненного цикла сборки Maven отвечает какой-то плагин. Один плагин может покрывать несколько этапов. Это не новость. Я надеюсь) Интересно то, что эти плагины не обязательно указывать в pom, вот минимальный рабочий pom, с которым ваш проект скомпилируется - https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Minimal_POM
Работает принцип convention over configuration.
А вот описание алгоритма определения нужного плагина: https://maven.apache.org/guides/introduction/introduction-to-plugin-prefix-mapping.html
Перескажу его вкратце.
Если вы указали при сборке фазу жизненного цикла, например, maven install, то определяются все необходимые шаги сборки и требуемые плагины как описано вот тут https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
А далее Maven ищет специальные файлы maven-metadata во всех подключенных к проекту репо, сливает их в один и ищет плагин по атрибуту prefix, который равен указаному вот тут как plugin https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#default-lifecycle-bindings-packaging-ejb-ejb3-jar-par-rar-war
Вот пример базового maven-metadata из Maven Central https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml
Как можно заметить, у большинства плагинов, поисковый префикс является частью artefactId. Это соглашение о наименовании, хотя и не обязательное. Т.е. всегда можно сделать свой maven-metadata-local.xml и привязать нужный плагин к нужной фазе там. Но я бы не рекомендовал так делать, т.к. это завязка на конкретный сборщик, которая хранится отдельно от кода вашего проекта. Также при создании плагина можно назвать его как угодно и указать к какой фазе сборки он привязан, может быть полезно, когда одним словом сложно выразить задачу плагина.
Если вы явно указываете цель для сборки, например, compiler:compile, то выполняется только эта цель, Maven пропускает разбор жизненного цикла сборки, сразу переходит к поиску нужного плагина в maven-metadata, в данном случае по префиксу compiler.
Вопрос - а как Maven определяет версию плагина? Есть еще один maven-metadata файлик с версиями у каждого плагина https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml
И как раз здесь лежит подводный камень: когда Maven-у понадобится конкретный плагин - он возьмет последнюю версию из тех, что найдет в подключенных к проекту репозиториях. А брать последнюю версию не всегда хорошо - там могут быть баги, или она просто может криво закачаться, если речь про внутренний прокси репозиторий.
Поэтому рекомендую начинать с минимальным POM, а перед выходом на ПРОМ фиксировать все версии плагинов.
Еще может возникнуть вопрос - а нельзя ли этот фокус провернуть с неофициальными плагинами? Ответ - можно, нужно лишь указать в settings.xml где еще искать maven-metadata.xml:
<pluginGroups>
<pluginGroup>org.codehaus.modello</pluginGroup>
</pluginGroups>
Еще момент - всегда можно добавить в pom конфигурацию плагина и там переопределить фазу сборки, на которой он запустится.
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
В примере цель из плагина jacoco-maven-plugin привяза к фазе test.
Выше я описывал, как работает Maven в режиме convention over configuration.
Продолжение следует...
#maven #buildtool #conv_over_conf
Сегодня расскажу о нескольких неочевидных особенностях работы Maven.
1) за каждый этап жизненного цикла сборки Maven отвечает какой-то плагин. Один плагин может покрывать несколько этапов. Это не новость. Я надеюсь) Интересно то, что эти плагины не обязательно указывать в pom, вот минимальный рабочий pom, с которым ваш проект скомпилируется - https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Minimal_POM
Работает принцип convention over configuration.
А вот описание алгоритма определения нужного плагина: https://maven.apache.org/guides/introduction/introduction-to-plugin-prefix-mapping.html
Перескажу его вкратце.
Если вы указали при сборке фазу жизненного цикла, например, maven install, то определяются все необходимые шаги сборки и требуемые плагины как описано вот тут https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
А далее Maven ищет специальные файлы maven-metadata во всех подключенных к проекту репо, сливает их в один и ищет плагин по атрибуту prefix, который равен указаному вот тут как plugin https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#default-lifecycle-bindings-packaging-ejb-ejb3-jar-par-rar-war
Вот пример базового maven-metadata из Maven Central https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml
Как можно заметить, у большинства плагинов, поисковый префикс является частью artefactId. Это соглашение о наименовании, хотя и не обязательное. Т.е. всегда можно сделать свой maven-metadata-local.xml и привязать нужный плагин к нужной фазе там. Но я бы не рекомендовал так делать, т.к. это завязка на конкретный сборщик, которая хранится отдельно от кода вашего проекта. Также при создании плагина можно назвать его как угодно и указать к какой фазе сборки он привязан, может быть полезно, когда одним словом сложно выразить задачу плагина.
Если вы явно указываете цель для сборки, например, compiler:compile, то выполняется только эта цель, Maven пропускает разбор жизненного цикла сборки, сразу переходит к поиску нужного плагина в maven-metadata, в данном случае по префиксу compiler.
Вопрос - а как Maven определяет версию плагина? Есть еще один maven-metadata файлик с версиями у каждого плагина https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml
И как раз здесь лежит подводный камень: когда Maven-у понадобится конкретный плагин - он возьмет последнюю версию из тех, что найдет в подключенных к проекту репозиториях. А брать последнюю версию не всегда хорошо - там могут быть баги, или она просто может криво закачаться, если речь про внутренний прокси репозиторий.
Поэтому рекомендую начинать с минимальным POM, а перед выходом на ПРОМ фиксировать все версии плагинов.
Еще может возникнуть вопрос - а нельзя ли этот фокус провернуть с неофициальными плагинами? Ответ - можно, нужно лишь указать в settings.xml где еще искать maven-metadata.xml:
<pluginGroups>
<pluginGroup>org.codehaus.modello</pluginGroup>
</pluginGroups>
Еще момент - всегда можно добавить в pom конфигурацию плагина и там переопределить фазу сборки, на которой он запустится.
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
В примере цель из плагина jacoco-maven-plugin привяза к фазе test.
Выше я описывал, как работает Maven в режиме convention over configuration.
Продолжение следует...
#maven #buildtool #conv_over_conf
maven.apache.org
Introduction to the POM – Maven
Всем привет!
Продолжим про Maven.
2) Несмотря на то, что к каждой фазе жизненного цикла по умолчанию привязана одна цель (goal) из конкретного плагина, на каждую фазу можно повесить сколько угодно целей. Порядок выполнения - как указано в pom.xml. Это еще одна одна причина, почему полезно явно обьявлять плагины в pom.
3) Если у вас в settings.xml указано несколько репозиториев с артифактами, и поиск по всем не дал результата - будет ошибка, но с некорректным текстом. В ней будет сказано, что артифакта нет в первом по списку репозитории
4) Если какие-то из репозиториев в settings.xml требуют аутентификации, данные для аутентификации были указаны и протухли, то вы узнаете об этом только тогда, когда понадобится обновить библиотеку, которая есть только в этом репозитории. Ошибка с аутентификацией будет в логах каждой сборки, но сборку не ломает
5) Может возникнуть вопрос - а зачем Maven каждый раз ходит по удаленным репозиториям, если зависимости не менялись. Это поведение контролируется параметром репозитория updatePolicy в settings.xml:
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2</url>
<releases>
<updatePolicy>always</updatePolicy>
</releases>
</repository>
Возможные значения: "always", "daily" (default), "interval:XXX" (in minutes) or "never".
Можно установить never, но есть два риска:
а) не будут подтягиваться новые snapshot,
б) не будут подтягиваться новые версии уже закачанных артифактов. А эту информацию индексирует IDEA и использует при AutoComplition
6) Если сохраненные локально зависимости были повреждены, то принудительно их перезагрузить можно через очистку локального репозитория, который находится в папке .m2. Командой mvn dependency:purge-local-repository, подробнее тут https://www.baeldung.com/maven-force-update или просто грохнув нужную папку в .m2
7) согласно уже упоминаемому ранее принципу convention over configuration по умолчанию в список репозиториев неявно включается Maven Central. Подробнее про settings.xml и его опции можно почитать тут https://habr.com/ru/post/339902/
8) список репозиториев можно задать как в pom.xml, так и в settings.xml. Предпочтительнее второй вариант, особенно при разработке библиотек, особенно внутренних. Причина: адрес репозитория может поменяться, а settings.xml проще обновить централизованно. Не говоря уже про библиотеки, которые придется перевыпускать. Конечно, если вы ходили по ссылке из предыдущего пункта - у нас есть возможность переопределить любой репозиторий из pom через зеркала\mirror в settings.xml. Причем при настройке зеркала можно не только указывать явно заменяемый репозиторий, но и использовать операторы * и ! Но это все же костыль)
#maven #buildtool #conv_over_conf
Продолжим про Maven.
2) Несмотря на то, что к каждой фазе жизненного цикла по умолчанию привязана одна цель (goal) из конкретного плагина, на каждую фазу можно повесить сколько угодно целей. Порядок выполнения - как указано в pom.xml. Это еще одна одна причина, почему полезно явно обьявлять плагины в pom.
3) Если у вас в settings.xml указано несколько репозиториев с артифактами, и поиск по всем не дал результата - будет ошибка, но с некорректным текстом. В ней будет сказано, что артифакта нет в первом по списку репозитории
4) Если какие-то из репозиториев в settings.xml требуют аутентификации, данные для аутентификации были указаны и протухли, то вы узнаете об этом только тогда, когда понадобится обновить библиотеку, которая есть только в этом репозитории. Ошибка с аутентификацией будет в логах каждой сборки, но сборку не ломает
5) Может возникнуть вопрос - а зачем Maven каждый раз ходит по удаленным репозиториям, если зависимости не менялись. Это поведение контролируется параметром репозитория updatePolicy в settings.xml:
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2</url>
<releases>
<updatePolicy>always</updatePolicy>
</releases>
</repository>
Возможные значения: "always", "daily" (default), "interval:XXX" (in minutes) or "never".
Можно установить never, но есть два риска:
а) не будут подтягиваться новые snapshot,
б) не будут подтягиваться новые версии уже закачанных артифактов. А эту информацию индексирует IDEA и использует при AutoComplition
6) Если сохраненные локально зависимости были повреждены, то принудительно их перезагрузить можно через очистку локального репозитория, который находится в папке .m2. Командой mvn dependency:purge-local-repository, подробнее тут https://www.baeldung.com/maven-force-update или просто грохнув нужную папку в .m2
7) согласно уже упоминаемому ранее принципу convention over configuration по умолчанию в список репозиториев неявно включается Maven Central. Подробнее про settings.xml и его опции можно почитать тут https://habr.com/ru/post/339902/
8) список репозиториев можно задать как в pom.xml, так и в settings.xml. Предпочтительнее второй вариант, особенно при разработке библиотек, особенно внутренних. Причина: адрес репозитория может поменяться, а settings.xml проще обновить централизованно. Не говоря уже про библиотеки, которые придется перевыпускать. Конечно, если вы ходили по ссылке из предыдущего пункта - у нас есть возможность переопределить любой репозиторий из pom через зеркала\mirror в settings.xml. Причем при настройке зеркала можно не только указывать явно заменяемый репозиторий, но и использовать операторы * и ! Но это все же костыль)
#maven #buildtool #conv_over_conf
Baeldung
Force Repository Update with Maven | Baeldung
Explore the options and goals of Maven that forcibly updates our local repository.
Всем привет!
Минигайд по кодировкам в Maven.
Для начала - главное, что нужно знать про кодировки в целом:
1) UTF-8 рулит)
2) нужно явно указывать кодировку.
Как это сделать в Maven?
1)
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Работает практически везде, за исключением пунктов 2) и 3) ниже.
2)
<properties>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
Работает для отчетных плагинов. Чтобы не выяснять для каких именно - проще установить и забыть)
3) Переменная среды JAVA_TOOL_OPTIONS равная -Dfile.encoding=UTF8
Работает тогда, когда Maven запускает отдельный Java процесс, в частности при запуске unit тестов из Maven. Причина падения тестов на Win, до этого успешно работающих на Mac\Lin. Падают из-за другой кодировки по умолчанию в Win.
4) Возможна точечная установка или переопределение кодировки для нужного плагина, например:
а) unit тесты
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
</configuration>
</plugin>
б) компиляция
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
Как видно из примеров - формат настроек у разных плагинов различается.
#maven
Минигайд по кодировкам в Maven.
Для начала - главное, что нужно знать про кодировки в целом:
1) UTF-8 рулит)
2) нужно явно указывать кодировку.
Как это сделать в Maven?
1)
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Работает практически везде, за исключением пунктов 2) и 3) ниже.
2)
<properties>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
Работает для отчетных плагинов. Чтобы не выяснять для каких именно - проще установить и забыть)
3) Переменная среды JAVA_TOOL_OPTIONS равная -Dfile.encoding=UTF8
Работает тогда, когда Maven запускает отдельный Java процесс, в частности при запуске unit тестов из Maven. Причина падения тестов на Win, до этого успешно работающих на Mac\Lin. Падают из-за другой кодировки по умолчанию в Win.
4) Возможна точечная установка или переопределение кодировки для нужного плагина, например:
а) unit тесты
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
</configuration>
</plugin>
б) компиляция
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
Как видно из примеров - формат настроек у разных плагинов различается.
#maven
Всем привет!
Еще немного про Maven. Есть несколько способов настроить версию Java в Maven, все они описаны в этой статье - https://www.baeldung.com/maven-java-version
Оптимальным начиная с Java 9 выглядит вариант с указанием
<properties>
<maven.compiler.release>9</maven.compiler.release>
</properties>
т.к. данное свойство заменяет собой три: source, target и bootclasspath. Последнее означает, что начиная с Java 9 все JDK умеют притворяться более ранними версиями с точностью до classpath, т.е. до предоставляемого API. А это важно при переходе на 11-ю и 17-ю Java - в первой выпилили Java EE модули, типа SOAP, во второй - закрыли internal пакеты.
Но это не все, что нужно знать про настройки версии JDK.
Если в проекте есть Kotlin - надежнее явно указать ему версию JDK. Для справки - старые версии Kotlin поддерживали только JDK 6 как target bytecode, потом был период JDK 8, сейчас поддерживается все версии https://kotlinlang.org/docs/maven.html
Настраивается так:
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<jvmTarget>1.9</jvmTarget>
По хорошему плагин Kotlin должен переиспользовать maven.compiler.release, но я не нашел подтверждающей это информации.
Второй тонкий момент связан с IDEA. IDEA смотрит на maven.compiler.release, в результате правильно проставляет:
а) Source language level для корневого и дочерних модулей Maven - это можно увидеть в Project Structure -> Modules
б) Target bytecode для всех модулей - Double Shift, далее вбиваем "java compiler".
Но есть еще одна важная опция Project Structure -> Project -> SDK. Она определяет версию JDK, на которой будет собираться проект. Повторюсь, из-за сильных изменений в структуре JDK как минимум в 11 и 17 версиях сборка на более новой JDK может сломаться. Причем из логов причина ошибки будет не ясна(
Так вот, IDEA в данном кейсе игнорирует настройки Maven. Прямого решения у проблемы я не нашел, судя по всему, ребята из IntelliJ считают, что корневой модуль в проекте Maven и проект IDEA это не одно и тоже. И это фича, а не баг)
Но есть обходной путь. Если все или большинство ваших проектов живут на определенной версии JDK, то ее можно выставить в IDEA по умолчанию. Настраивается вот здесь: Double Shift, далее вбиваем "structure for new projects"
#maven #java #kotlin
Еще немного про Maven. Есть несколько способов настроить версию Java в Maven, все они описаны в этой статье - https://www.baeldung.com/maven-java-version
Оптимальным начиная с Java 9 выглядит вариант с указанием
<properties>
<maven.compiler.release>9</maven.compiler.release>
</properties>
т.к. данное свойство заменяет собой три: source, target и bootclasspath. Последнее означает, что начиная с Java 9 все JDK умеют притворяться более ранними версиями с точностью до classpath, т.е. до предоставляемого API. А это важно при переходе на 11-ю и 17-ю Java - в первой выпилили Java EE модули, типа SOAP, во второй - закрыли internal пакеты.
Но это не все, что нужно знать про настройки версии JDK.
Если в проекте есть Kotlin - надежнее явно указать ему версию JDK. Для справки - старые версии Kotlin поддерживали только JDK 6 как target bytecode, потом был период JDK 8, сейчас поддерживается все версии https://kotlinlang.org/docs/maven.html
Настраивается так:
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<jvmTarget>1.9</jvmTarget>
По хорошему плагин Kotlin должен переиспользовать maven.compiler.release, но я не нашел подтверждающей это информации.
Второй тонкий момент связан с IDEA. IDEA смотрит на maven.compiler.release, в результате правильно проставляет:
а) Source language level для корневого и дочерних модулей Maven - это можно увидеть в Project Structure -> Modules
б) Target bytecode для всех модулей - Double Shift, далее вбиваем "java compiler".
Но есть еще одна важная опция Project Structure -> Project -> SDK. Она определяет версию JDK, на которой будет собираться проект. Повторюсь, из-за сильных изменений в структуре JDK как минимум в 11 и 17 версиях сборка на более новой JDK может сломаться. Причем из логов причина ошибки будет не ясна(
Так вот, IDEA в данном кейсе игнорирует настройки Maven. Прямого решения у проблемы я не нашел, судя по всему, ребята из IntelliJ считают, что корневой модуль в проекте Maven и проект IDEA это не одно и тоже. И это фича, а не баг)
Но есть обходной путь. Если все или большинство ваших проектов живут на определенной версии JDK, то ее можно выставить в IDEA по умолчанию. Настраивается вот здесь: Double Shift, далее вбиваем "structure for new projects"
#maven #java #kotlin
Baeldung
Setting the Java Version in Maven | Baeldung
Learn about different ways to set the Java version for your Maven project
Всем привет!
Три лайфхака по работе с Maven:
1) чтобы заработала отладка в IDEA при запуске через Maven к примеру unit тестов:
для версий surefire плагина >= 2.14: use -DforkCount=0
http://maven.apache.org/plugins/maven-surefire-plugin/examples/debugging.html
2) запустить цель (goal) на дочернем модуле и всех его детях: maven --projects artifactId --also-make-dependents
Полезно на больших многомодульных проектах. Подбробнее https://maven.apache.org/guides/mini/guide-multiple-modules-4.html
Запускать цель напрямую но дочернем проекте нельзя, т.к в таком случае не применятся настройки из корневого проекта - как Maven, так и к примеру Lombok.
Еще важно - перед тем, как отлаживать дочерний проект нужно проинсталлировать в локальный Maven репозиторий те модули, от которых он зависит. Я про maven install.
3) по умолчанию maven следует принципу fail fast и это правильно) Но если нужно понять, в каком состоянии проект - можно запустить полную сборку игнорирую промежуточные ошибки до окончания сборки: maven --fail-at-end
#maven #java
Три лайфхака по работе с Maven:
1) чтобы заработала отладка в IDEA при запуске через Maven к примеру unit тестов:
для версий surefire плагина >= 2.14: use -DforkCount=0
http://maven.apache.org/plugins/maven-surefire-plugin/examples/debugging.html
2) запустить цель (goal) на дочернем модуле и всех его детях: maven --projects artifactId --also-make-dependents
Полезно на больших многомодульных проектах. Подбробнее https://maven.apache.org/guides/mini/guide-multiple-modules-4.html
Запускать цель напрямую но дочернем проекте нельзя, т.к в таком случае не применятся настройки из корневого проекта - как Maven, так и к примеру Lombok.
Еще важно - перед тем, как отлаживать дочерний проект нужно проинсталлировать в локальный Maven репозиторий те модули, от которых он зависит. Я про maven install.
3) по умолчанию maven следует принципу fail fast и это правильно) Но если нужно понять, в каком состоянии проект - можно запустить полную сборку игнорирую промежуточные ошибки до окончания сборки: maven --fail-at-end
#maven #java
maven.apache.org
Maven Surefire Plugin – Debugging Tests
Всем привет!
Сегодня пост про очередной типичный вопрос на собеседовании.
Звучит он так: расскажите про жизненный цикл сборки Maven, он же build lifecycle.
Я вкратце уже писал про это https://t.me/javaKotlinDevOps/10. Но т.к. типичный ответ на собеседовании - перечисление циклов и фаз сборки, то хочется раскрыть тему. На самом деле вопрос про другое - в чем суть цикла сборки?
1) набор фаз сборки JVM проекта уже придуман и зафиксирован умными людьми) из команды Maven в виде Default Lifecycle https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference Т.е. считается, что список покрывает все потребности сборки. Как пример продуманности могу привести фазы pre-integration-test и post-integration-test, которые нужны для того, чтобы поднять и опустить контейнер сервлетов, менеджер очередей или БД до и после фазы интеграционных тестов (integration-test). Для обычных тестов (test) таких вспомогательных фаз нет.
2) последовательность фаз также зафиксирована, порядок менять нельзя да и нет смысла
3) большинство фаз являются опциональными, запускаются только если к фазе подключен хоть один плагин и либо в проекте присутствуют определенные файлы, либо есть настройки плагина, указывающие что ему делать - например, generate-sources или компиляция Kotlin
4) на одной фазе могут работать несколько целей (goal) из разных плагинов - например, компиляция Java и Kotlin на фазе compile или несколько тестовых фреймворков на фазе test
5) запуская определенный шаг сборки - mvn install - вы запускаете все предыдущие шаги цикла. И в большинстве случаев это правильно. Если такое поведение не требуется - нужно запустить конкретную цель у конкретного плагина, для моего примера это mvn install:install (плагин:цель). Т.е. тут совпадают название фазы сборки, плагина и цели. Так бывает не всегда, например, для фазы test-compile цель выглядит так compiler:testCompile
Итого: цель жизненного цикла сборки - стандартизировать процесс сборки.
Да и в принципе стандартизация - конек Maven. Тут и типовой набор папок https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html, и цикл сборки, и сложность написания своего кода для сборки, и достаточно богатый набор стандартных плагинов, для подключения которых ничего не надо делать (если быть точным - неплохо бы зафиксировать версию в pom), и архетипы, и конвенция по наименованию артефактов, и наконец центральный репозиторий - https://mvnrepository.com/repos/central
#maven #interview_question
Сегодня пост про очередной типичный вопрос на собеседовании.
Звучит он так: расскажите про жизненный цикл сборки Maven, он же build lifecycle.
Я вкратце уже писал про это https://t.me/javaKotlinDevOps/10. Но т.к. типичный ответ на собеседовании - перечисление циклов и фаз сборки, то хочется раскрыть тему. На самом деле вопрос про другое - в чем суть цикла сборки?
1) набор фаз сборки JVM проекта уже придуман и зафиксирован умными людьми) из команды Maven в виде Default Lifecycle https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference Т.е. считается, что список покрывает все потребности сборки. Как пример продуманности могу привести фазы pre-integration-test и post-integration-test, которые нужны для того, чтобы поднять и опустить контейнер сервлетов, менеджер очередей или БД до и после фазы интеграционных тестов (integration-test). Для обычных тестов (test) таких вспомогательных фаз нет.
2) последовательность фаз также зафиксирована, порядок менять нельзя да и нет смысла
3) большинство фаз являются опциональными, запускаются только если к фазе подключен хоть один плагин и либо в проекте присутствуют определенные файлы, либо есть настройки плагина, указывающие что ему делать - например, generate-sources или компиляция Kotlin
4) на одной фазе могут работать несколько целей (goal) из разных плагинов - например, компиляция Java и Kotlin на фазе compile или несколько тестовых фреймворков на фазе test
5) запуская определенный шаг сборки - mvn install - вы запускаете все предыдущие шаги цикла. И в большинстве случаев это правильно. Если такое поведение не требуется - нужно запустить конкретную цель у конкретного плагина, для моего примера это mvn install:install (плагин:цель). Т.е. тут совпадают название фазы сборки, плагина и цели. Так бывает не всегда, например, для фазы test-compile цель выглядит так compiler:testCompile
Итого: цель жизненного цикла сборки - стандартизировать процесс сборки.
Да и в принципе стандартизация - конек Maven. Тут и типовой набор папок https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html, и цикл сборки, и сложность написания своего кода для сборки, и достаточно богатый набор стандартных плагинов, для подключения которых ничего не надо делать (если быть точным - неплохо бы зафиксировать версию в pom), и архетипы, и конвенция по наименованию артефактов, и наконец центральный репозиторий - https://mvnrepository.com/repos/central
#maven #interview_question
Telegram
(java || kotlin) && devOps
Всем привет!
Хочу рассказать собрать в одном посте несколько мало и среднеизвестных фичей Maven.
Поехали!
Для начала немного теории. Жизненный цикл сборки = фиксированная последовательность фаз сборки. К фазе сборки можно подключить плагин(ы), а точнее…
Хочу рассказать собрать в одном посте несколько мало и среднеизвестных фичей Maven.
Поехали!
Для начала немного теории. Жизненный цикл сборки = фиксированная последовательность фаз сборки. К фазе сборки можно подключить плагин(ы), а точнее…
Всем привет!
Продолжая тему Maven. Когда я изучал документацию по Maven, то наткнулся такую несколько странную возможность, как отключение компиляции через опцию -Dmaven.main.skip.
Почему, зачем? На первый взгляд - бессмысленно.
Но недавно на практике понял зачем она нужна.
Предположим в вашем pom есть некий плагин с достаточно сложными нестандартными настройками. Есть процесс, в котором нужно выполнить его goal. Если мы просто запустим goal командой mvn plugin:version:goal, то настройки из pom будут проигнорированы. Предположим, задать их из командной строки невозможно, т.к. плагин это не поддерживает. А даже если возможно - это будет дублирование, нарушение DRY, т.е. тоже плохой вариант)
Хуже всего то, что наш плагин находится в build lifecycle https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference после фазы compile. Пусть проект уже собран, но как известно инкрементальная компиляция в Maven существует, но с ней есть нюансы: https://stackoverflow.com/questions/16963012/maven-compiler-recompile-all-files-instead-modified/19653164#19653164
К слову - именно по скорости компиляции Maven сильно проигрывает Gradle.
Так вот - именно тут опция -Dmaven.main.skip и выходит на сцену, сильно уменьшая время выполнения.
P.S. Да, по моему опыту - плагин для компиляции Kotlin, увы, ее не поддерживает.
#maven #java #kotlin
Продолжая тему Maven. Когда я изучал документацию по Maven, то наткнулся такую несколько странную возможность, как отключение компиляции через опцию -Dmaven.main.skip.
Почему, зачем? На первый взгляд - бессмысленно.
Но недавно на практике понял зачем она нужна.
Предположим в вашем pom есть некий плагин с достаточно сложными нестандартными настройками. Есть процесс, в котором нужно выполнить его goal. Если мы просто запустим goal командой mvn plugin:version:goal, то настройки из pom будут проигнорированы. Предположим, задать их из командной строки невозможно, т.к. плагин это не поддерживает. А даже если возможно - это будет дублирование, нарушение DRY, т.е. тоже плохой вариант)
Хуже всего то, что наш плагин находится в build lifecycle https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference после фазы compile. Пусть проект уже собран, но как известно инкрементальная компиляция в Maven существует, но с ней есть нюансы: https://stackoverflow.com/questions/16963012/maven-compiler-recompile-all-files-instead-modified/19653164#19653164
К слову - именно по скорости компиляции Maven сильно проигрывает Gradle.
Так вот - именно тут опция -Dmaven.main.skip и выходит на сцену, сильно уменьшая время выполнения.
P.S. Да, по моему опыту - плагин для компиляции Kotlin, увы, ее не поддерживает.
#maven #java #kotlin
maven.apache.org
Introduction to the Build Lifecycle – Maven
Всем привет!
Есть интересная тема - инструменты сборки для JVM проектов. А в рамках нее другая горячая тема - управление конфликтами зависимостей. Когда в проект подтягивается, как правило транзитивно, две версии одной и той же зависимости. А должна остаться только одна)
Отличное сравнение 3 систем сборки по управлению конфликтами зависимостей еще 10+ лет назад проведено в этой статье: https://habr.com/ru/companies/jugru/articles/191246/
Вывод из статьи - в Maven все сделано, скажет так, странно)))
Приходится явно указывать нужную версию каждой конфликтной зависимости в проекте.
Первый вопрос, который приходит на ум - зачем в Maven так сделали и когда собираются исправлять.
Ответ тут - https://stackoverflow.com/questions/34201120/maven-set-dependency-mediation-strategy-to-newest-rather-than-nearest
Спойлер - исправлять не собираются, считают, что так сборка будет более предсказуемой и повторяющейся. Т.е. описанный выше подход - запускай приложение, находи конфликты в runtime и указывай явно версию в своем модуле - считается правильным. Но есть лайфхак - см. ответ на stackoverflow.
Ну а чтобы найти версии проблемной зависимости - нужен mvn dependency:tree. О его "секретных" (на самом деле полезных) ключах этой таски Maven можно почитать тут https://www.digitalocean.com/community/tutorials/maven-dependency-tree-resolving-conflicts
Ну и если хочется копнуть глубже, например понять, как разрешается конфликт scope-ов зависимости или узнать про то, как разработчик библиотеки может уменьшить возможность появления конфликта (optional) - см. главный источник истины по Maven - его документацию https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
#maven #gradle #java #buildtool #dependency_management
Есть интересная тема - инструменты сборки для JVM проектов. А в рамках нее другая горячая тема - управление конфликтами зависимостей. Когда в проект подтягивается, как правило транзитивно, две версии одной и той же зависимости. А должна остаться только одна)
Отличное сравнение 3 систем сборки по управлению конфликтами зависимостей еще 10+ лет назад проведено в этой статье: https://habr.com/ru/companies/jugru/articles/191246/
Вывод из статьи - в Maven все сделано, скажет так, странно)))
Приходится явно указывать нужную версию каждой конфликтной зависимости в проекте.
Первый вопрос, который приходит на ум - зачем в Maven так сделали и когда собираются исправлять.
Ответ тут - https://stackoverflow.com/questions/34201120/maven-set-dependency-mediation-strategy-to-newest-rather-than-nearest
Спойлер - исправлять не собираются, считают, что так сборка будет более предсказуемой и повторяющейся. Т.е. описанный выше подход - запускай приложение, находи конфликты в runtime и указывай явно версию в своем модуле - считается правильным. Но есть лайфхак - см. ответ на stackoverflow.
Ну а чтобы найти версии проблемной зависимости - нужен mvn dependency:tree. О его "секретных" (на самом деле полезных) ключах этой таски Maven можно почитать тут https://www.digitalocean.com/community/tutorials/maven-dependency-tree-resolving-conflicts
Ну и если хочется копнуть глубже, например понять, как разрешается конфликт scope-ов зависимости или узнать про то, как разработчик библиотеки может уменьшить возможность появления конфликта (optional) - см. главный источник истины по Maven - его документацию https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
#maven #gradle #java #buildtool #dependency_management
Хабр
Разрешение конфликтов в транзитивных зависимостях — Хороший, Плохой, Злой
Вместо предисловия В ближайшую субботу мы с EvgenyBorisov будем выступать в Питере на JUG.ru . Будет много веселого трэша и интересной инфы (иногда не разберешь, где проходит граница), и одно из моих...
Всем привет!
Вдогонку ко вчерашней теме про управление зависимостями.
В Maven есть две фичи, смягчающие боль по управлению зависимостями:
1) bom - Bill Of material - pom-ник, объявляющий версии протестированного и гарантированного рабочего набора обязательных зависимостей, например, библиотеки Spring Security
2) dependencyManagemenet - возможность в одном месте, как правило это корневой pom, объявить все версии используемых в проекте библиотек. Туда же можно подключать уже готовые bom-ы. После этого задача поднятия версии какой-то зависимости по всему многомодульному проекту упрощается
Есть ли что-то подобное в Gradle?
Да, есть.
Вот тут описывается как "сэмулировать" bom в Gradle - https://habr.com/ru/articles/784784/
Ключевые слова platform и constraints https://docs.gradle.org/current/userguide/dependency_constraints.html
Почему я говорю сэмулировать: если посмотреть на структуру bom - это типичный Maven xml файл. Gradle публикует bom в двух форматах - maven для совместимости и свой json, вот пример https://repo1.maven.org/maven2/io/github/mfvanek/pg-index-health-bom/0.10.2/
Но на самом деле Gradle умеет чуть больше - есть такая штука, как catalog - по сути позволяющая структурировать зависимости в древовидную структуру и дать имя-ссылку каждому уровню. См. https://docs.gradle.org/current/userguide/platforms.html Причем объявлять завимости можно как в build.gradle, так и в отдельном toml файле. Каталог можно использовать сам по себе - как набор версий, так и ограничивать с помощью него версии транзитивных зависимостей - через те же constraints и platform.
#gradle #maven #depenedency_management #buildtool
Вдогонку ко вчерашней теме про управление зависимостями.
В Maven есть две фичи, смягчающие боль по управлению зависимостями:
1) bom - Bill Of material - pom-ник, объявляющий версии протестированного и гарантированного рабочего набора обязательных зависимостей, например, библиотеки Spring Security
2) dependencyManagemenet - возможность в одном месте, как правило это корневой pom, объявить все версии используемых в проекте библиотек. Туда же можно подключать уже готовые bom-ы. После этого задача поднятия версии какой-то зависимости по всему многомодульному проекту упрощается
Есть ли что-то подобное в Gradle?
Да, есть.
Вот тут описывается как "сэмулировать" bom в Gradle - https://habr.com/ru/articles/784784/
Ключевые слова platform и constraints https://docs.gradle.org/current/userguide/dependency_constraints.html
Почему я говорю сэмулировать: если посмотреть на структуру bom - это типичный Maven xml файл. Gradle публикует bom в двух форматах - maven для совместимости и свой json, вот пример https://repo1.maven.org/maven2/io/github/mfvanek/pg-index-health-bom/0.10.2/
Но на самом деле Gradle умеет чуть больше - есть такая штука, как catalog - по сути позволяющая структурировать зависимости в древовидную структуру и дать имя-ссылку каждому уровню. См. https://docs.gradle.org/current/userguide/platforms.html Причем объявлять завимости можно как в build.gradle, так и в отдельном toml файле. Каталог можно использовать сам по себе - как набор версий, так и ограничивать с помощью него версии транзитивных зависимостей - через те же constraints и platform.
#gradle #maven #depenedency_management #buildtool
Хабр
Создание и использование BOM в Gradle
Привет Хабр! В каждой компании (а если она крупная, то, скорее всего, в каждом подразделении) должна быть выстроена культура использования BOM ( bill of materials ) для управления версиями...
Всем привет!
Если посмотреть на формат pom файлов Maven https://maven.apache.org/pom.html#POM_Reference, то можно увидеть там несколько свойств с метаданными проекта - organization, developers, contributors, scm, issueManagement... Плюс есть возможность получить информацию о дереве зависимостей проекта через команду mvn dependency:tree. Т.е. некоторую информацию о проекте собрать можно. Но собирать ее воедино придется руками. И это информация о проекте, но не о конкретной сборке этого проекта.
А что, если у нас другой сборщик? Или не Java проект? Или АС, включающая несколько компонентов? А можно ли сохранить информацию о найденных в проекте уязвимостях?
Для всего этого есть https://cyclonedx.org/use-cases/
СycloneDX позволяет сохранить информацию о проекте в объектной модели, которую можно сохранить в JSON, XML или Protocol Buffers. И кажется, что его разработчики решили дать возможность собрать всю возможную информацию:
1) состав проекта - библиотеки, приложения, веб-сервисы, драйверы, файлы... Есть средства группировки компонентов проекта.
2) информацию о разработчиках проекта - людях и компании
3) хэши для контроля целостности
4) информация о лицензиях
5) release notes, в т.ч. информация о commit, из которого собрана сборка, и фичах, вошедших в релиз
6) информация об уязвимостях, найденных в компонентах проекта, а также ссылка на патч, исправляющий уязвимость. Последнее нужно для того случая, когда накатить исправление невозможно из-за dependency hell. Информация об уязвимостях может быть получена как в привязке к зависимостям проекта, так и просто в виде списка.
7) дерево зависимостей
8) ссылки на сайты, чаты, почту...
9) любые произвольные метаданные в формате ключ-значение
И это только то, с чем я успел разобраться)
Есть интеграция с множеством сборщиков https://cyclonedx.org/tool-center/
Как по мне - хороший пример стандартизации. Немного смущает огромное число типов метаданных, но с другой стороны - не обязательно использовать все сразу.
#metadata #release_notes #maven #standardization
Если посмотреть на формат pom файлов Maven https://maven.apache.org/pom.html#POM_Reference, то можно увидеть там несколько свойств с метаданными проекта - organization, developers, contributors, scm, issueManagement... Плюс есть возможность получить информацию о дереве зависимостей проекта через команду mvn dependency:tree. Т.е. некоторую информацию о проекте собрать можно. Но собирать ее воедино придется руками. И это информация о проекте, но не о конкретной сборке этого проекта.
А что, если у нас другой сборщик? Или не Java проект? Или АС, включающая несколько компонентов? А можно ли сохранить информацию о найденных в проекте уязвимостях?
Для всего этого есть https://cyclonedx.org/use-cases/
СycloneDX позволяет сохранить информацию о проекте в объектной модели, которую можно сохранить в JSON, XML или Protocol Buffers. И кажется, что его разработчики решили дать возможность собрать всю возможную информацию:
1) состав проекта - библиотеки, приложения, веб-сервисы, драйверы, файлы... Есть средства группировки компонентов проекта.
2) информацию о разработчиках проекта - людях и компании
3) хэши для контроля целостности
4) информация о лицензиях
5) release notes, в т.ч. информация о commit, из которого собрана сборка, и фичах, вошедших в релиз
6) информация об уязвимостях, найденных в компонентах проекта, а также ссылка на патч, исправляющий уязвимость. Последнее нужно для того случая, когда накатить исправление невозможно из-за dependency hell. Информация об уязвимостях может быть получена как в привязке к зависимостям проекта, так и просто в виде списка.
7) дерево зависимостей
8) ссылки на сайты, чаты, почту...
9) любые произвольные метаданные в формате ключ-значение
И это только то, с чем я успел разобраться)
Есть интеграция с множеством сборщиков https://cyclonedx.org/tool-center/
Как по мне - хороший пример стандартизации. Немного смущает огромное число типов метаданных, но с другой стороны - не обязательно использовать все сразу.
#metadata #release_notes #maven #standardization
maven.apache.org
POM Reference – Maven
Всем привет!
Есть такое известное правило - DRY - Don't Repeat Yourself. Оно касается кода, оно касается и настроек. Окей, задали мы настройки в одном месте - например, в корневом pom файле или build.gradle(.kts). Как пробросить их во все необходимые файлы при сборке? Этот процесс называется property expansion. К слову - в Maven есть понятие resource filtering. Это нечто большее - проход по всем ресурсным файлам, их фильтрация и кастомизация. Но обычно как раз таки используется для property expansion. Так вот, нашел неплохую статью как сделать это в Maven и Gradle https://www.baeldung.com/spring-boot-auto-property-expansion Что интересно - решение Maven выглядит более продуманным.
#gradle #maven #buildtool
Есть такое известное правило - DRY - Don't Repeat Yourself. Оно касается кода, оно касается и настроек. Окей, задали мы настройки в одном месте - например, в корневом pom файле или build.gradle(.kts). Как пробросить их во все необходимые файлы при сборке? Этот процесс называется property expansion. К слову - в Maven есть понятие resource filtering. Это нечто большее - проход по всем ресурсным файлам, их фильтрация и кастомизация. Но обычно как раз таки используется для property expansion. Так вот, нашел неплохую статью как сделать это в Maven и Gradle https://www.baeldung.com/spring-boot-auto-property-expansion Что интересно - решение Maven выглядит более продуманным.
#gradle #maven #buildtool
Baeldung
Automatic Property Expansion with Spring Boot | Baeldung
Learn about the property expansion mechanism provided by Spring through Maven and Gradle build methods.
Всем привет!
Как можно собрать Docker образ?
Есть плагины для Maven\Gradle, позволяющие использовать всю мощь соответствующих инструментов при сборке.
А есть собственно Docker и команда docker build, собирающая образ на основании Dockerfile. Ну и еще Docker Compose, позволяющий описать, собрать и запустить группу образов, полезный при локальной разработки или если нет k8s.
До какого-то момента возможности Docker по кастомизации процесса сборки были скажем так базовыми.
Но как вы уже догадываетесь - все поменялось.
Появился Docker Bake. Вот хорошая статья на Хабре https://habr.com/ru/articles/886938/
Команда для сборки: docker buildx bake
buildx - это расширенный вариант build, позволяющий выбрать движок для сборки образа.
В последние версии Docker функционал Bake включен по умолчанию.
Основные плюсы с примерами конфигураций:
1) функции
function "tag" {
params = [name, tag]
result = ["${name}:${tag}"]
}
2) группировка образов для сборки одной командой
group "default" {
targets = ["frontend", "api"]
}
target "frontend" {
...
target "api" {
2) наследование конфигурации
target "app-base" {
dockerfile = "Dockerfile"
args = {
BASE_IMAGE = "node:16-alpine"
}
}
target "app-dev" {
inherits = ["app-base"]
...
}
3) кросс-платформенная сборка
target "app-all" {
platforms = ["linux/amd64", "linux/arm64"]
}
4) профили сборки
variable "ENV" {
default = "dev"
}
group "default" {
targets = ["app-${ENV}"]
}
target "app-dev" {
...
target "app-stage" {
..
target "app-prod" {
...
и сборка командой
ENV=prod docker buildx bake
5) матричная сборка - сборка всех возможных комбинаций, указанных в матрице
target "app" {
matrix = {
platform = ["linux/amd64", "linux/arm64"]
version = ["1.0", "2.0"]
}
6) встроенное распараллеливание сборки
7) чтение переменных из файла
// Импорт переменных из JSON-файла
variable "settings" {
default = {}
}
target "app" {
dockerfile = "Dockerfile"
args = {
CONFIG = "${settings.app_config}"
}
}
Ну и главное - декларативность описания процесса сборки, описываем результат, а не действия. За исключением пользовательских функций, конечно)
Кажется, неплохая альтернатива плагинам и хороший апгрейд Docker. Важным плюсом видится встраивание в Docker. Тот же Scaffold - выполняющий другую функцию, но в т.ч. собирающий образы - нужно ставить отдельно, и в IDEA он тянет за собой кучу лишних фичей.
P.S. Docker Compose заменяет только в части сборки. И может с ним интегрироваться.
#docker #gradle #maven
Как можно собрать Docker образ?
Есть плагины для Maven\Gradle, позволяющие использовать всю мощь соответствующих инструментов при сборке.
А есть собственно Docker и команда docker build, собирающая образ на основании Dockerfile. Ну и еще Docker Compose, позволяющий описать, собрать и запустить группу образов, полезный при локальной разработки или если нет k8s.
До какого-то момента возможности Docker по кастомизации процесса сборки были скажем так базовыми.
Но как вы уже догадываетесь - все поменялось.
Появился Docker Bake. Вот хорошая статья на Хабре https://habr.com/ru/articles/886938/
Команда для сборки: docker buildx bake
buildx - это расширенный вариант build, позволяющий выбрать движок для сборки образа.
В последние версии Docker функционал Bake включен по умолчанию.
Основные плюсы с примерами конфигураций:
1) функции
function "tag" {
params = [name, tag]
result = ["${name}:${tag}"]
}
2) группировка образов для сборки одной командой
group "default" {
targets = ["frontend", "api"]
}
target "frontend" {
...
target "api" {
2) наследование конфигурации
target "app-base" {
dockerfile = "Dockerfile"
args = {
BASE_IMAGE = "node:16-alpine"
}
}
target "app-dev" {
inherits = ["app-base"]
...
}
3) кросс-платформенная сборка
target "app-all" {
platforms = ["linux/amd64", "linux/arm64"]
}
4) профили сборки
variable "ENV" {
default = "dev"
}
group "default" {
targets = ["app-${ENV}"]
}
target "app-dev" {
...
target "app-stage" {
..
target "app-prod" {
...
и сборка командой
ENV=prod docker buildx bake
5) матричная сборка - сборка всех возможных комбинаций, указанных в матрице
target "app" {
matrix = {
platform = ["linux/amd64", "linux/arm64"]
version = ["1.0", "2.0"]
}
6) встроенное распараллеливание сборки
7) чтение переменных из файла
// Импорт переменных из JSON-файла
variable "settings" {
default = {}
}
target "app" {
dockerfile = "Dockerfile"
args = {
CONFIG = "${settings.app_config}"
}
}
Ну и главное - декларативность описания процесса сборки, описываем результат, а не действия. За исключением пользовательских функций, конечно)
Кажется, неплохая альтернатива плагинам и хороший апгрейд Docker. Важным плюсом видится встраивание в Docker. Тот же Scaffold - выполняющий другую функцию, но в т.ч. собирающий образы - нужно ставить отдельно, и в IDEA он тянет за собой кучу лишних фичей.
P.S. Docker Compose заменяет только в части сборки. И может с ним интегрироваться.
#docker #gradle #maven
Хабр
Docker Bake: современный подход к сборке контейнеров
Традиционный способ сборки Docker-образов с помощью команды docker build прост и понятен, но при работе с комплексными приложениями, состоящими из множества компонентов, этот процесс может стать...
С чем проще работать - Maven vs Gradle?
Как ни странно - ответ на этот вопрос не очевиден.
Если говорить о чтении глазами разработчиком - Gradle выглядит попроще т.к. там почти нет boilerplate кода. И тот, и другой инструмент придерживаются принципа convention over configuration, но Maven проигрывает из-за xml.
Но если проект начинает развиваться - что-то может пойти не так) Gradle позволяет легко выстрелить себе в ногу создав очень сложный скрипт деплоя. Причем много кода появляется там, где его не ожидаешь - в build скрипте. И этот код не на Java, т.е. может отличаться от кода проекта. А если добавить сюда multiproject - разбираться в этом становится сложно. Maven выносит весь императивный код в плагины, скрипты сборки чисто декларативные.
И можно глянуть с другой стороны - со стороны tooling, например, той же IDEA. Тут победа на стороне Maven - распарсить xml легко, а вот чтобы понять структуру Gradle проекта - нужно начать его исполнять. Это и дольше, и процесс подвержен ошибками, а в теории и уязвимостям.
Вывод - оба инструмента хороши, имеют свои плюсы и минусы. Рекомендую изучить оба. Я для себя не определился)
#buildtool #maven #gradle #conv_over_conf
Как ни странно - ответ на этот вопрос не очевиден.
Если говорить о чтении глазами разработчиком - Gradle выглядит попроще т.к. там почти нет boilerplate кода. И тот, и другой инструмент придерживаются принципа convention over configuration, но Maven проигрывает из-за xml.
Но если проект начинает развиваться - что-то может пойти не так) Gradle позволяет легко выстрелить себе в ногу создав очень сложный скрипт деплоя. Причем много кода появляется там, где его не ожидаешь - в build скрипте. И этот код не на Java, т.е. может отличаться от кода проекта. А если добавить сюда multiproject - разбираться в этом становится сложно. Maven выносит весь императивный код в плагины, скрипты сборки чисто декларативные.
И можно глянуть с другой стороны - со стороны tooling, например, той же IDEA. Тут победа на стороне Maven - распарсить xml легко, а вот чтобы понять структуру Gradle проекта - нужно начать его исполнять. Это и дольше, и процесс подвержен ошибками, а в теории и уязвимостям.
Вывод - оба инструмента хороши, имеют свои плюсы и минусы. Рекомендую изучить оба. Я для себя не определился)
#buildtool #maven #gradle #conv_over_conf