Подписаться на ленту

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2024 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2024.1.3.2953
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Сборка Java-проекта с использованием Maven
Этот урок освещает создание вами простого Java-приложения с использованием Maven.
Что вы создадите
Вы создадите простое приложение и соберете его с помощью Maven.
Что вам потребуется
- Примерно 15 минут свободного времени
- Любимый текстовый редактор или IDE
- JDK 6 и выше
Как проходить этот урок
Как и большинство уроков по Spring, вы можете начать с нуля и выполнять каждый шаг, либо пропустить базовые шаги, которые вам уже знакомы. В любом случае, вы в конечном итоге получите рабочий код.
Чтобы начать с нуля, перейдите в Настройка проекта.
- Загрузите и распакуйте архив с кодом этого урока, либо кнонируйте из репозитория с помощью Git: git clone https://github.com/spring-guides/gs-maven.git
- Перейдите в каталог gs-maven/initial
- Забегая вперед, установите Maven
Когда вы закончите, можете сравнить получившийся результат с образцом в gs-maven/complete .
Настройка проекта
Для начала вам необходимо настроить Java-проект перед тем, как собрать его Maven’ом. Т.к. урок посвящен Maven, сделаем проект максимально простым, насколько это возможно.
Создание структуры каталогов
В выбранном вами каталоге проекта создайте следующую структуру каталогов; к примеру, командой mkdir -p src/main/java/hello для *nix систем:
+-- src +-- main +-- java +-- hello
Внутри src/main/java/hello директории вы можете создать любые Java-классы, какие вы хотите. Для простоты и согласованности с остальной частью урока, Spring рекомендует вам создать два класса: HelloWorld.java и Greeter.java .
package hello; public class HelloWorld < public static void main(String[] args) < Greeter greeter = new Greeter(); System.out.println(greeter.sayHello()); >>
package hello; public class Greeter < public String sayHello() < return "Hello world!"; >>
Теперь, когда у вас есть проект, который вы можете собрать с Maven, вам нужно установит сам Maven.
Maven можно получить, скачав zip-файл с maven.apache.org/download.cgi. Необходимы только бинарные файлы, так что ищите ссылку на архив с именем apache-maven-version-bin.zip или apache-maven-version-bin.tar.gz.
Распакуйте архив и добавьте путь к каталогу bin в переменную окружения path.
Чтобы протестировать правильность установки Maven, запустите в командной строке:
mvn -v
Если всё было сделано правильно, то вы увидите сообщение примерно такого содержания:
Apache Maven 3.0.5 (r01de14724cdef164cd33c7c8c2fe155faf9602da; 2013-02-19 07:51:28-0600) Maven home: /usr/share/maven Java version: 1.7.0_09, vendor: Oracle Corporation Java home: /Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home/jre Default locale: en_US, platform encoding: UTF-8 OS name: "mac os x", version: "10.8.3", arch: "x86_64", family: "mac"
Теперь у вас есть установленный Maven.
Создание простой сборки Maven
Теперь, когда Maven установлен, вам необходимо создать определение Maven-проекта. Maven-проекты определяются как XML-файлы с названием pom.xml. Помимо всего прочего, этот файл определяет имя проекта, версию, а также зависимости от сторонних библиотек.
Создайте файл с названием pom.xml в корневом каталоге проекта и наполните его следующим содержанием:
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
org.springframework
gs-maven
jar
0.1.0
org.apache.maven.plugins
maven-shade-plugin
2.1
package
shade
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
hello.HelloWorld
За исключением дополнительного элемента , это простейший из pom.xml файлов, необходимый для сборки Java проекта. Он включает следующие детали конфигурации проекта:
- — версия POM-модели (всегда 4.0.0)
- — группа или организация, к которой принадлежит проект. Чаще всего выражается в виде перевернутого наоборот доменного имени
- — имя, которое будет передано библиотеке экземпляра(artifact) проекта (к примеру, имя его JAR или WAR файла)
- — версия, с которой будет собран проект
- — как проект должен быть упакован. По умолчанию, с «jar» упаковывается в JAR-файл, «war» — WAR-файл
Когда речь заходит о выборе схемы управления версиями, Spring рекомендует [семантическое управление версиями] semver.org подход.
На данном этапе мы имеем минимальное, но уже рабочее определение Maven-проекта.
Сборка Java кода
Теперь все готово для сборки проекта Maven’ом. Вы можете выполнить несколько этапов жизненного цикла сборки, включая компиляцию кода, создание библиотеки пакета(такого, как JAR-файл) и установку библиотеки в локальный репозиторий Maven зависимостей.
Попробуйте собрать, выполнив команду, приведенную ниже:
mvn compile
Этим вы запустите Maven, передав ему указание на выполнение задачи compile. Когда он закончит, вы должны найни скомпилированные .class файлы в target/classes директории.
Вряд ли вы захотите распостранять или работать напрямую с .class файлами, поэтому вам полее подойдет выполнение задачи package:
mvn package
Задача package включает компиляцию вашего Java кода, запуск тестов, а в конце упаковывает в JAR-файл в target директории. Название JAR-файла будет основано на и . К примеру, с минимальным pom.xml(см. выше), JAR-файл будет иметь название gs-maven-initial-0.1.0.jar.
Если вы изменили значение
Maven также хранит репозиторий зависимостей на вашей локальной машине(обычно в .m2/repository директории в вашей домашней папке) для быстрого доступа к зависимостям проекта. Если вы хотите добавить JAR-файл вашего проекта в локальный репозиторий, тогда вам необходимо выполнить задачу install :
mvn install
Задача install включает компиляцию, тестирование, упаковку кода проекта, а затем копирование в локальный репозиторий, тем самым другие проекты смогут ссылаться на него как на зависимость.
Говоря о зависимостях, пришло время объявлять зависимости в Maven сборке.
Объявление зависимостей
Простой «Hello World» пример полностью автономный и не зависит от каких-либо дополнительных библиотек. Однако, большинство приложений зависит от внешних библиотек, с реализацией распостраненного и/или сложного функционала.
К примеру, предположим, что в дополнение к «Hello World!» вы хотите, чтобы приложение печатало текущую дату и время. Вы могли бы использовать функциональность из стандартных(native) Java библиотек, но мы можем сделать это и другими интересными способами, например с помощью Joda Time библиотеки.
Для начала, изменим HelloWorld.java , как показано ниже:
package hello; import org.joda.time.LocalTime; public class HelloWorld < public static void main(String[] args) < LocalTime currentTime = new LocalTime(); System.out.println("The current local time is: " + currentTime); Greeter greeter = new Greeter(); System.out.println(greeter.sayHello()); >>
Здесь HelloWorld использует Joda Time LocalTime класс для получения и печати текущего времени.
Если бы вы запустили mvn compile для сборки проекта сейчас, то получили бы ошибку сборки, потому что вы не объявили Joda Time компилируемую зависимость в сборке. Вы можете это исправить, добавив следующие строки в pom.xml(в пределах элемента):
joda-time
joda-time
2.2
Этот блок XML объявляет список зависимостей проекта. В частности, он объявляет единственную зависимость от Joda Time библиотеки. В элементе, зависимость определяется через описание трех вложенных элементов:
- — группа или организация, к которой принадлежит зависимость.
- — необходимая библиотека
- — версия необходимой библиотеки
По умолчанию, все зависимости определены как зависимости. Т.е. они должны быть доступны во время компиляции(а если вы собираете WAR-файл, то в /WEB-INF/lib каталоге). Кроме того, вы можете добавить элемент, с одним из значений:
- provided — зависимости, которые требуются для компиляции кода проекта, но которые будут доступны во время выполнения кода контейнером(например, Java Servlet API)
- test — зависимости, которые используются для компиляции и запуска тестов, но не требуемые для сборки или выполнения кода проекта
Сейчас, если вы выполните mvn compile или mvn package , Maven должен будет разрешить Joda Time зависимость из Maven Central репозитория и успешно собрать проект.
Здесь полная версия pom.xml :
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
org.springframework
gs-maven
jar
0.1.0
joda-time
joda-time
2.2
org.apache.maven.plugins
maven-shade-plugin
2.1
package
shade
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
hello.HelloWorld
Полная версия pom.xml использует Maven Shade Plugin как удобный инструмент для создание выполняемого JAR-файла. Целью данного урока является показать, как начать работать с Maven, не используя, в частности, этот плагин.
Итог
Поздравляем! Вы создали простой, но эффективный файл сборки Maven для сборки Java проектов.
С оригинальным текстом урока вы можете ознакомиться на spring.io.
Apache Maven
Сегодня речь пойдёт о Maven. Это в первую очередь утилита для сборки Java проектов, но по факту является системой управления проекта в целом, определяет его структуру и, во многом, жизненный цикл. (Apache Maven is a software project management and comprehension tool. Project object model (POM)) Работая с базовыми сценариями в хорошо настроенном окружении, программисту не требуется глубоко вникать в тонкости этой технологии. Но стоит проекту Maven перестать собираться, как легко можно попасть в тупик, для выхода из которого требуется опыт и понимание определённых нюансов. Почему не находится плагин? Зачем он лезет в интернет? Почему падает деплой артефакта? Откуда взялась библиотека log4j первой версии? Почему сборка работает локально, а на Jenkins падает? У меня в IDE всё компилируется, а Maven что-то тупит. Вспоминать и фантазировать можно долго.
Многие скажут, что я утрирую, но Вы можете поверить, что люди переходили с Eclipse на IDEA после трёх и более лет разработки потому, что сложный maven проект отказывался нормально импортироваться в Eclipse, но коллеги пользовались IDEA и никто не мог помочь настроить его в Eclipse?
Но даже учитывая всё это, использование Maven — это необходимость и меньшее из зол. Maven заставляет всех разработчиков делать одинаково и более менее правильно. Это касается структуры проектов, расположения и имён определённых файлов, управлениями модулями, зависимостями и в определённой степени процессом релизов.
Как развивались некоторые проекты, пока их не мигрировали на maven? Все известные мне проекты собирались с помощью утилиты Ant. Не знаю, смог ли кто-то настроить компиляцию и сборку с использованием непосредственно утилиты javac, поставляемой вместе JDK а также shell или bat скриптов, но это было бы уж слишком безумно. Большинство вполне успешно обходилось утилитой Ant.
Давайте пофантазируем, как это было. Куда положить исходный код? Ну, допустим ./src/Main.java или ./src/com/example/Main.java, если с пакетами. Куда проперти файлы? Давайте туда же ./src/log4j.xml. Но не все, что-то положим в ./conf. Куда будем складывать скомпилированные файлы? Мне нравится ./dist. А тесты? В ./test/src, а данные для теста в ./test/*, то есть рядом с исходниками. Честно говоря, я подсматриваю в реальном проекте. Если вы уже имеете опыт разработки, то уже чувствуете отсутствие стандарта — вроде всё логично, но в то же время наобум. И на каждом новом проекте будет похоже, но по-другому. Кстати, а библиотеки мы куда положим? В ./lib, конечно же. Стоп! Что значит положим библиотеки, разве они не должны храниться централизованно и выкачиваться автоматически при необходимости? Нет, библиотеки скачивают, потом обычно из переименовывают из log4j-1.2.12.jar в log4j.jar, чтобы версию уже никак нельзя было определить, разве что её заботливо указали в MANIFEST файле внутри библиотеки. Естественно, библиотеки сохраняются и в системе контроля версий, и таким образом любой проект весит уже никак не меньше 20-50 мегабайт.
С библиотеками вообще очень неудобно — начинаешь новый проект и каждую библиотеку скачивать из интернета и сохранять в проекте неудобно, ещё и может понадобиться в IDE явно её добавлять. Но был найден удобный и эффективный подход, решающий все проблемы — мы будем копировать lib из проекта в проект. Со временем там окажутся все нужные нам библиотеки: логгеры, драйверы всех баз данных, spring, apache commons, junit, сервлеты.
Ну вот у нас есть исходные файлы, мы настроили IDE, указав пути к ним, добавили библиотеки. Пришло время показать наше приложение кому-то, а на местном наречии — «выложить». Выложить в тестовую среду или продакшен. В случае web приложения это означает, что нужно создать ZIP архив фиксированной структуры, состоящий из скомпиливанных классов, файлов настроек, библиотек и ещё пары специальных файлов. Этот архив называется WAR-архивом (или варником). Для всего этого и создаётся Ant сборка. Чаще всего это файл с названием build.xml, в довесок к которому идёт build.properties. Пример содержимого такого фэйла:

Команда завершается успешно, в папке ./target появился файл samples-maven-simple-1.0-SNAPSHOT.jar. В нём только сам pom.xml и MANIFEST.MF следующего содержания: Чаще всего это применяется к библиотекам логирования. Например, исключаем отовсюду log4j, а потом добавляем log4j-over-slf4j. Подробнее тут.
Добавлю один интересный сценарий связанный с тестированием, о котором не все знают. Все тесты располагаются в папке src/test, в все юнит тесты имеют постфикс «Test». По практикам разработки существуют ещё интеграционные тесты, которые могут использовать внешние сервисы и запускаются отдельно от юнит тестов. Для них иногда заводят отдельные модули и настройки surefire плагина. Можно поступить иначе — добавить к имени теста постфикс «IT» (полный список: **/IT*.java, **/*IT.java, **/*ITCase.java). Такие тесты запускаются командой «mvn verify», а отвечает за них специальный maven-failsafe-plugin. Он требует явного указания в pom.xml: То есть при настройках по умолчанию придётся ждать целый день, чтобы воспользоваться исправленной версией библиотеки! А если установить опцию ‘always’, то сборки начнут заметно тормозить при большом количестве snapshot зависимостей. Удобно при необходимости добавлять к аргументам команды сборки параметр «-U» (mvn clean package -U), тогда все снэпшоты будут принудительно обновлены. Процесс разработки можно выстроить следующим образом:
- Устанавливаем версию библиотеки 1.0-SNAPSHOT
- Устанавливаем версию зависимости в основном приложении в 1.0-SNAPSHOT
- Вносим изменения в библиотеку, выполняем deploy
- Пересобираем основное приложение, при необходимости с аргументом -U, тестируем
- Повторяем шаги 3-4 до тех пор, пока код не стабилизируется и все ошибки не будут исправлены
- Устанавливаем версию библиотеки 1.0
- Устанавливаем версию зависимости в основном приложении в 1.0
- Осуществляем финальные проверки и выходим в релиз. Если находим ошибки, возвращаемся на шаг 6, меняя версию на 1.0.1
- Начинаем работу над следующим релизом
- Устанавливаем версию библиотеки 1.1-SNAPSHOT
- Устанавливаем версию зависимости в основном приложении в 1.1-SNAPSHOT
Чтобы снэпшоты не накапливались в репозитории в неограниченном количестве, в Nexus и Artifactory существуют политика удаления старых версий. Поэтому иногда старые снэпшоты теряют свою актуальность или вообще пропадают из репозитория, хотя по хорошему последняя-то версия не должна удаляться. Следует руководствоваться следующим правилом: версия release не должна иметь snapshot зависимостей. Только в этом случае релизная версия представляет собой что-то финальное и не подверженное случайным факторам. Если же мы оперируем версиями snapshot, значит наш код часто меняется и при случае мы легко можем восстановить артефакт в репозитории, запустив команду deploy.
На этом пока оставим релизы и снэпшоты. Заметили, что вместе с обычным артефактом сборки в репозиторий попала ещё версия «jar-with-dependencies»? При этом группа, артефакт и версия у них совпадают. Это возможно благодаря дополнительному атрибуту зависимостей — classifier. При использовании assembly плагина в файле конфигурации assembly.xml нужно указать id. В нашей сборке id неявно устанавливается в «jar-with-dependencies». Результаты сборки плагином assembly ведут себя так же, как и основной артефакт. Зависимость будет выглядеть так, если понадобится:
hipravin.samples samples-maven-simple 1.0-SNAPSHOT jar-with-dependencies
Атрибут classifier также используется при загрузке исходного кода и документации javadoc. По умолчанию загружается только скомпилированный код, а пользователи нашей библиотеки не увидят никакой документации, а если попытаются посмотреть исходный код, то получат в лучшем случае декомпилированную версию. Это не очень удобно, поэтому лучше публиковать sources и javadoc. Для этого добавляем в pom.xml два плагина:
org.apache.maven.plugins maven-source-plugin 3.2.1 attach-sources jar org.apache.maven.plugins maven-javadoc-plugin 3.2.0 attach-javadocs jar
Теперь при выполнении команды deploy в репозиторий будут загружены дополнительные артефакты с классификаторами «-sources» и «-javadoc», и соответствующая информация будет автоматически доступна в IDE разработчика, использующего нашу библиотеку.
Модули
До сих пор мы работали с приложением, состоящим из одного модуля. Однако модули — едва ли не главная функциональность Maven, сильнее всего влияющая на процесс проектирования и разработки приложений. Сегодня в эпоху микросервисов становится всё более популярно использовать один репозиторий (GIT) для одного сервиса. В этом случае объём кода и логики одного приложения часто не требует разделения на модули. Сейчас я не буду никак сравнивать монолит с микросервисной архитектурой. Многие работают с монолитом или как минимум c приложениями с большим объёмом кода в одном репозитории. В этом случае разделение кода на модули имеет тот же смысл, что разделение на пакеты, классы, методы.
Разделение на пакеты — по большей части логическое, визуальное, если не считать модификаторов доступа protected и default. При разделение на модули код одного модуля ни во время компиляции, ни во время исполнения ничего не знает о других модулях, если не установлена зависимость. Одна из ситуаций, когда модули жизненно необходимы — если в одном проекте уживаются вместе несколько приложений. Например, несколько web приложений, которые мы собираем в отдельные WAR архивы и может даже развертываем на разных серверах Apache Tomcat. Каждое приложение мы помещаем в отдельный модуль. Приложения не полностью различны, они как-то перекликаются, относятся к единому бизнес домену, поэтому возникают повторяющиеся классы и методы, то есть дублирование кода. Тогда мы выделяем отдельный модуль common, куда перемещаем все общие части. Потом нам хочется больше модулей, чтобы каждый отвечал за свою задачу, а не содержал сборную солянку разных утилит. Тогда в дополнение к common мы вводим модули security, model, dao и так далее. Просто чтобы в коде было чисто и аккуратно. Бонусом получаем скорость сборки, ведь нет смысла пересобирать модули, в которых не было изменений.
Код примера. Рассмотрим небольшое приложение, состоящее из нескольких модулей. Программа подсчитывает частоту появления различных слов во входном файле. В модуле common реализован сам алгоритм, модуль consoleapp содержит главный класс для запуска из консоли, а модуль webapp — Web приложение с REST сервисом. Между webapp и сonsoleapp нет зависимостей, но оба зависят от common. Можно представить, что приложение существует давно, а для работы с ним всегда использовалась консоль, но теперь решили добавить ещё и веб сервис. В коде приложения ничего показательного, его я приводить не буду, лучше сконцентрируюсь на Maven.
При создании проекта в корне я сразу удалил папку src, а в pom.xml установил свойство packaging в значение «pom». Также artifactId имеет окончание «-parent», но это необязательно, больше для удобства и потому что так принято.
hipravin.samples.maven samples-maven-multiple-parent 1.0-SNAPSHOT pom
Такой модуль называют родительским (parent) или иногда основным, главным. Он обычно не содержит исходного кода и артефактов сборки. Его предназначение — управлять остальными модулями. Все они должны быть перечислены в теге modules:
common consoleapp webapp
Все дочерние (child) модули наследуют свойства, зависимости, плагины от родительского модуля. Например, уровень языка для compiler плагина достаточно указать только в главном модуле. А вот зависимости в главном модуле указывать не стоит, потому что исключить их в дочерних модулях будет крайне затруднительно. Вместо этого в родительском модуле фиксируют список библиотек их версий, а в дочерних — лишь ссылаются на них. Выглядит это так: в главном модуле в pom.xml используется тег dependencyManagement, а в дочерних — dependency без версии:
. org.junit.jupiter junit-jupiter-engine 5.4.0 test org.junit.jupiter junit-jupiter-engine
Помимо перечисления версий библиотек в dependencyManagement по одной, существует дополнительный механизм указания версий для целой группы зависимостей — BOM (Bill Of Materials). Это очень полезно для проектов с большим количеством модулей (например, для spring: core, context, beans, web, jdbc, . ). Модуль webapp использует Spring Boot, BOM можно указать в главном модуле следующим образом:
org.springframework.boot spring-boot-dependencies 2.2.6.RELEASE pom import . org.springframework.boot spring-boot-starter-web
Ключевым здесь является значение параметра scope равное import.
Для проектов, использующих Spring Boot альтернативный вариант — указать в качестве родительского проекта spring-boot-starter-parent. То есть родительский модуль не обязательно должен располагаться рядом в том же проекте, он может загружаться и из удалённого Maven репозитория. В этом случае нужно установить свойство relativePath в пустое значение. Например, так:
org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE
При этом родительский модуль может быть только один.
Чтобы классы из модуля common были доступны в модуле consoleapp, нужно добавить зависимость так же, как ранее мы добавляли зависимость на библиотеку jackson.
hipravin.samples.maven common $
Вместо версии 1.0-SNAPSHOT мы ссылаемся на версию родительского модуля, потому что неразумно иметь разные версии в рамках одного проекта, а дублировать эту версию многократно неудобно. На этом этапе может возникнуть определённая путаница, как в понимании происходящего, так и в работе самого Maven. Как мы знаем, зависимости загружаются из репозитория, либо берутся напрямую из папки .m2. Но мы ещё ни разу не собирали наш проект и тем более не выполняли install или deploy. В рамках одного проекта Maven в этом не нуждается — он выстроит дерево зависимостей наших модулей, причём именно дерево, а не граф, потому что циклы запрещены. Потом он осуществит сборку модулей в правильном порядке и при работе с каждым модулем все его зависимости уже будут обработаны. Если какой-то из модулей Maven упорно пытается искать в репозитории, то вероятно допущена ошибка в координатах зависимости.
Чтобы собрать весь проект целиком достаточно запустить команду «mvn package» в корне проекта. Так же с install и deploy. Так выглядит лог успешной сборки:
. [INFO] Reactor Summary for samples-maven-multiple-parent 1.0-SNAPSHOT: [INFO] [INFO] samples-maven-multiple-parent . SUCCESS [ 1.012 s] [INFO] common . SUCCESS [ 10.054 s] [INFO] consoleapp . SUCCESS [ 1.032 s] [INFO] webapp . SUCCESS [ 4.769 s] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS
Довольно редко возникает необходимость собирать только один модуль, однако если проект громоздкий и полный билд занимает минуты, то это может быть полезно. Для этого нужно указать список модулей для сборки, например один модуль webapp, а также параметр —also-make, чтобы были обработаны необходимые модули, от которых явно или неявно зависит webapp.
clean package --projects webapp --also-make . [INFO] Reactor Summary for samples-maven-multiple-parent 1.0-SNAPSHOT: [INFO] [INFO] samples-maven-multiple-parent . SUCCESS [ 0.382 s] [INFO] common . SUCCESS [ 6.051 s] [INFO] webapp . SUCCESS [ 2.755 s] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS
Заметим, что сейчас в проекте несколько раз дублируется версия родительского модуля. Это вполне допустимо, но при обновлении версии на, скажем, 1.0 или 1.1-SNAPSHOT, нам придётся обновлять код в нескольких местах, что создаёт вероятность ошибки, вызванной человеческим фактором. Хуже всего обновлять по памяти: тут, тут и тут, потому что легко можно случайно забыть или пропустить один из модулей. Если артефакты попадают в локальный репозиторий, то проект продолжит собираться без ошибок, но код одного из модулей будет использовать старую версию родительского модуля. Второй вариант — использовать автозамену по проекту (Ctrl+Shift+R в IDEA). Этот вариант плох тем, что можно случайно заменить лишнего — но чаще всего в этом случае проект просто не скопмилируется. Правильный способ обновлять версию — использовать плагин versions:
mvn versions:set -DnewVersion=1.1-SNAPSHOT mvn versions:commit
Команда «commit» здесь не имеет ничего общего с коммитом в системе контроля версий, это специфический этап работы плагина, удаляющий сохранённую копию pom.xml файла, которая создаётся на первом этапе.
Разное
О нескольких моментах стоит упомянуть для полноты картины, хоть им не нашлось места в демонстрационных проектах, описанных ранее.
Параметр optional. Модули webapp и consoleapp транзитивно зависят от всех библиотек, от которых зависит common. Эти библиотеки можно исключить в pom.xml каждого из этих модулей, используя exclusions, как мы уже видели. Альтернативно можно указать на этих зависимостях в модуле common параметр optional в значение true, тогда в webapp и consoleapp изменения не потребуются. Используется редко, не буду заострять внимание на этом.
Беспорядок с версиями зависимостей. Версия одной и той же библиотеки может быть указана явно единожды, транзитивно единожды, явно многократно, транзитивно многократно. Теоретически у Maven есть детерминированный алгоритм по определению версии. Практически лучше избегать неопределённости и изучать граф зависимостей, а также список библиотек, попадающий в артефакт сборки JAR with dependencies, WAR или EAR. Версия, указанная явно в pom.xml самого модуля имеет приоритет над транзитивными версиями. Однако если версии отличаются ещё и в group id / artifact id как, например, log4j и log4j2, то проблему можно решить только аккуратным исключением всех лишних зависимостей. А найти проблему можно, опять же, только анализом графа зависимостей и артефактов сборки.
Dependency scope. Для каждой зависимости можно указать scope. Часть значений влияет на то, в какие classpath попадает данная зависимость, другие просто определяют некое особое поведение. Мы уже встречали compile (значение по умолчанию), test и import. Я просто приведу список всех значений с небольшими комментариями. Я буду писать «сохраняется в lib» имея в виду, что библиотека попадает в classpath во время исполнения, а также копируется в директорию lib внутри артефактов сборки таких как WAR и EAR.
compile Значение по умолчанию. Зависимость доступна во время компиляции основного кода и тестов, сохраняется в lib. provided Зависимость доступна во время компиляции основного кода и тестов, но не сохраняется в lib. Применяется, когда библиотека предоставляется контейнером. Например, Weblogic предоставляет драйвер для соединения с базой данных. runtime Зависимость не доступна во время компиляции основного кода, доступна для компиляции тестов (не знаю зачем), но сохраняется в lib. Пример — драйвер базы данных, библиотеки логирования. test Зависимость не доступна во время компиляции основного кода, доступна для компиляции тестов, не сохраняется в lib. system Позволяет подключить библиотеку, jar файл которой располагается по определённому пути на файловой системе. Не рекомендую к использованию, в этом случае следует просто установить библиотеку в локальный репозиторий с помощью install-file. import Используется в dependencyManagement вместе c так называемым BOM (bill of materials)
Я не разбираю scope подробно, потому что в большинстве случаев достаточно compile и test, которые тривиальны, а остальные применяются по ситуации и редко приводят к скрытым ошибкам. А вот понять и запомнить чем отличается provided от runtime при первом знакомстве мало кому удаётся.
Жизненный цикл, фазы. Жизненный цикл состоит из фаз, которые мы можем указывать в строке запуска mvn. Каждый плагин запускается в ту фазу, которая указана в его конфигурации. Список всех фаз: validate, compile, test, package, verify, install, deploy. Опять же углубляться не буду, полагаю станет только непонятней. С практической точки зрения мы уже рассмотрели все основные фазы жизненного цикла.
Архетипы. Без использования IDE чтобы создать пустой maven проект нужно будет где-то взять заголовок xml файла и добавить в него как минимум координаты проекта. А если наш проект использует какой-то фреймворк, то потребуются ещё какие-нибудь обязательные настройки и файлы. В Maven существует понятие архетипа — способа создавать готовые проекты по шаблону с указанием набора параметров. Spring initializer, вероятно, внутри работает на основе архетипов. Однако это отдельный сайт, да и ещё со встроенной поддержкой в IDEA, поэтому пользоваться шаблонами Spring Boot через интерфейс командной строки было бы странно. В своей практике я не применял архетипы кроме как в ознакомительных целях.
Gradle. Gradle — аналог Maven, который появился чуть позже и считается более продвинутым, современным, стильным — модным — молодежным. Основное различие между ними — Gradle использует язык Groovy или Kotlin для конфигурации, а не XML, а также по-другому определяет жизненный цикл. Одно и то же приложение может одновременно иметь эквивалентные конфигурации сборок на Maven и Gradle. На сайте spring.io примеры одновременно содержат инструкции и для Maven, и для Gradle. При этом сам springframework начиная как минимум с версии 4 собирается с помощью Gradle. В работе же я встречал только Maven, если не считать одного приложения, которое потом перенесли на Maven для порядка и потому что Gradle билд сломался, а починить никто не сумел. По своему опыту могу только сказать, что Gradle очень плохо настраивается в окружении, где отсутствует или ограничен доступ в интернет. Не исключено, что при должной сноровке это возможно, но у кого она есть, эта сноровка. Я уверен, что в коммерческой разработке Maven ещё долго будет популярен благодаря старым проектам и наработанному специалистами опыту.
Заключение
В Mаven очень много нюансов и тонкостей, но в целом это очень стройная и эффективная технология и экосистема. Так или иначе, в мире Java разработки встречи с Maven не избежать. Надеюсь, что наиболее частые, полезные и хитрые сценарии я как-нибудь, да затронул.
Ответы на вопросы на собеседование Maven (часть 1).
Сборка (англ. assembly) — двоичный файл, содержащий исполняемый код программы или другой, подготовленный для использования информационный продукт.
Автоматизация сборки — этап написания скриптов или автоматизация широкого спектра задач применительно к ПО, применяемому разработчиками в их повседневной деятельности, включая такие действия, как:
- Компиляция исходного кода в бинарный код
- Сборка бинарного кода
- Выполнение тестов
- Разворачивание программы на производственной платформе
- Написание сопроводительной документации или описание изменений новой версии
- Что такое Maven? Как он работает?
Apache Maven — это фреймворк для автоматизации сборки проектов, компиляции, создания jar, создания дистрибутива программы, генерации документации. Если собирать большие проекты с командной строки, то команда для сборки будет очень длинной, поэтому её иногда записывают в bat/sh скрипт. Но такие скрипты зависят от платформы. Для того чтобы избавиться от этой зависимостии и упростить написание скрипта используют инструменты для сборки проекта.
Maven, обеспечивает декларативную, а не императивную сборку проекта. То есть, в файлах проекта pom.xml содержится его декларативное описание, а не отдельные команды. Все задачи по обработке файлов Maven выполняется через плагины.
- Какие преимущества Maven?
Основные преимущества Maven:
- Независимость от OS. Сборка проекта происходит в любой операционной системе. Файл проекта один и тот же.
- Управление зависимостями. Редко какие проекты пишутся без использования сторонних библиотек(зависимостей). Эти сторонние библиотеки зачастую тоже в свою очередь используют библиотеки разных версий. Maven позволяет управлять такими сложными зависимостями. Что позволяет разрешать конфликты версий и в случае необходимости легко переходить на новые версии библиотек.
- Возможна сборка из командной строки. Такое часто необходимо для автоматической сборки проекта на сервере (Continuous Integration).
- Хорошая интеграция со средами разработки. Основные среды разработки на java легко открывают проекты которые собираются c помощью maven. При этом зачастую проект настраивать не нужно — он сразу готов к дальнейшей разработке.
- Как следствие — если с проектом работают в разных средах разработки, то maven удобный способ хранения настроек. Настроечный файл среды разработки и для сборки один и тот же — меньше дублирования данных и соответственно ошибок.
- Декларативное описание проекта.
- Какие недостатки Maven?
Недостатки Maven:
- Неочевидность. Если в Ant указывается команда на удаление — и удаляется файл, то в случае Maven надо всем сердцем довериться плагину и документации по нему.
- При таком объёме необходимых знаний документации не так много, особенно по каким-то специальным моментам. Да и просто читать придётся много. Порог вхождения, если потребуется собирать даже не самое сложное приложение куда выше, чем у Ant.
- Если нужно найти какой-то специальный плагин — это будет сделать непросто, плагинов много. И не факт, что найденный подойдёт на все 100% и будет работать без ошибок.
- Нужен доступ в интернет (или придётся разворачивать собственный репозиторий, что трудоёмко)
- Большие трудности, если проект не типовий.
- Какими аспектами управляет Maven?
Вот основные аспекты, которыми позволяет управлять Maven:
- Создание (Build)
- Документирование (Documentation)
- Отчёты (Reporting)
- Зависимости (Dependencies)
- Релизы (Releases)
- SCM
- Список рассылки (Mailing list)
- Дистрибьюция (Distribution)
- Как узнать какую версию Maven вы используете?
С помощью следующий команди:
mvn —version
- Для чего создан Maven?
Основной целью Maven является предоставление разработчику:
- Понятной модели для проектов, которая может быть использовано повторно и была бы проста в поддержании.
- Плагины, которые могут взаимодействовать с этой моделью.
Структура и сожержание проекта Maven указывается в специальном xml-файле, который назывется Project Object Model (POM), который является базовым модулем всей системы.
- Какая структура каталогов в Maven?
В Maven стандартная структура каталогов, благодаря ей отпадает необходимость прописывать пути к файлам проекта. В корневом каталоге проекта находится pom.xml и несколько текстовых файлов. Всё остальное хозяйство аккуратно разложено в подкаталогах. Главные из них — src и target. Однако, порядок сохраняется и вглубь:

- Где вы хранятся файлы классов при компиляции проекта Maven?
Файлы классов хранятся в: $
- Что такое pom.xml?
pom.xml — это XML-файл, который содержит информацию о деталях проекта, и конфигурации используемых для создания проекта на Maven. Он всегда находится в базовом каталоге проекта. Этот файл также содержит задачи и плагины. Во время выполнения задач, Maven ищет файл pom.xml в базовой директории проекта. Он читает его и получает необходимую информацию, после чего выполняет задачи.
Корневой элемент
- Какую информацию содержит pom.xml?
Среди информации которую содержит pom.xml мы можем выделить следующие:
- Зависимости проекта (project dependencies)
- Плагины (plugins)
- Задачи/цели (goals)
- Профиль создания (build profiles)
- Версия проекта (project version)
- Разработчики (developers)
- Список рассылки (mailing list)
- Что такое супер POM?
Все POM — файлы являются наследниками родительского pom.xml. Этот POM-файл называется Super POM и содержит значения, унаследованные по умолчанию.
- Какие элементы необходимы для минимального POM?
Требуемые элементы для минимального POM ето корневий елемент, modelVersion, GroupID, artifactID и версия. Минимальный POM файл:

- Что такое зависимости в Maven?
Зависимость (dependency) — это те библиотеки, которые непосредственно используются в вашем проекте для компиляции кода или его тестирования.
- Что такое артефакт в Maven?
Артефакт (artefact) — это, по сути, любая библиотека, хранящаяся в репозитории (месте хранения). Это может быть какая-то зависимость или плагин. Обычно артефактом является JAR-файл, который хранится в репозитории Maven. Каждый артефакт содержит group ID, artifact ID и версию.
- Что такое плагин в Maven?
Плагин (plugin) — это зависимости Maven’а, расширяющие его функционал.
- Что такое задача в Maven?
Задача (goal) — это специальная задача, которая относится к сборке проекта и его управлению. Она может привязываться как к нескольким фазам, так и ни к одной. Задача, которая не привязана ни к одной фазе, может быть запущена вне фаз сборки с помощью прямого вызова.
- Что такое архетип в Maven?
Архетип (archetype) — это некая стандартная компоновка файлов и каталогов в проектах различного рода (веб, swing-проекты и прочие). Другими словами, Maven знает, как обычно строятся проекты и в соответствии с архетипом создает структуру каталогов.
- Что такое репозиторий в Maven?
Репозиторий (repository) — глобальное хранилище всех библиотек, доступных для Maven, это место где хранятся артефакты: jar файлы, pom-файлы, javadoc, исходники, плагины.
- Какие типы репозитория существуют в Maven?
В Maven существуют три типы репозитория:
- Локальный (local) репозиторий — это директория, которая хранится на нашем компьютере. Она создаётся в момент первого выполнения любой команды Maven. По умолчанию она расположена в /.m2/repository — персональная для каждого пользователя.
- Центральный (central) репозиторий — это репозиториий, который обеспечивается сообществом Maven. Он содержит огромное количество часто используемых библиотек. Который расположен в http://repo1.maven.org/maven2/ и доступен на чтение для всех пользователей в интернете. Если Maven не может найти зависимости в локальном репозитории, то автоматически начинается поиск необходимых файлов в центральном репозитории
- Удалённые (remote) репозиторий — иногда, Maven не может найти необходимые зависимости в центральном репозитории. В этом случае, процесс сборки прерывается и в консоль выводится сообщение об ошибке. Для того, чтобы предотвратить подобную ситуацию, в Maven предусмотрен механизм Удалённого репозитория, который является репозиторием, который определяется самим разработчиком. Там могут храниться все необходимые зависимости.
- Какая команда установляет JAR-файл в локальное хранилище?
mvn install
- Какой порядок поиска зависимостей Maven?
Когда мы выполняем собрку проекта в Maven, автоматически начинается поиск необходимых зависимостей в следующем порядке:
- Поиск зависимостей в локальном репозитории Если зависимости не обнаружены, происходит переход к шагу 2.
- Поиск зависимостей в центральном репозитории. Если они не обнаружены и удалённый репозиторий определён, то происходит переход к шагу 4.
- Если удалённый репозиторий не определён, то процесс сборки прекращается и выводится сообщение об ошибке.
- Поиск зависимостей на удалённом репозитории, если они найдены, то происходит их загрузка в локальный репозиторий, если нет — выводится сообщение об ошибке.
- Какие два файла настройки есть в Maven, как они называются и где расположены?
В Maven, файлы настройки называются settings.xml, и они расположены в двох местах:
- Каталог где установлен Maven: $M2_Home/conf/settings.xml
- Домашняя директория пользователя: $/.m2/settings.xml
- Что такое жизненный цикл сборки в Maven?
Жизненный цикл сборки(Lifecycle) — это чётко опредлённая последовательность фаз во время выполнения которых должын быть достигнуты определённые цели. Здесь фаза представляет собой стадию жизненного цикла.
- Назовите основные фазы жизненного цикла сборки Maven?
Когда Maven начинает сборку проекта, он проходит через определённую последовательность фаз сборки, и выполняет определенные задачи, которые указаны в каждой из фаз.
В Maven есть следующие 3 стандартных жизненных цикла:
- Очистка (clean) — очищает артефакты, созданные до сборки.
- Сборка (default or build) — используется для создания приложения.
- Создание сайта проекта (site) — генерирует документацию сайта для проекта.
- Что делает команда mvn site?
mvn site — создает веб-сайт проекта.
- Что делает команда mvn clean?