Прямой ответ
Maven выполняет фазы жизненного цикла в фиксированном порядке, а зависимости разрешаются по координатам из репозиториев. Вызов фазы выполняет все предыдущие фазы.
| Команда | Выполняемые фазы | Результат |
|---|---|---|
mvn compile |
validate → compile | Классы в target/classes |
mvn test |
… → test | Отчёт о тестах |
mvn package |
… → package | Артефакт в target |
mvn install |
… → install | Артефакт в локальном репозитории |
mvn -B clean package
mvn -B dependency:tree
mvn -B -DskipTests package
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>
</dependencies>
Порядок фаз фиксирован, поэтому вызов поздней фазы выполняет все предыдущие и пропустить промежуточный шаг нельзя.
Жизненный цикл и его фазы
Фаза определяет шаг сборки, а привязка плагина задаёт действие, выполняемое на этом шаге.
Фиксированный порядок. Фазы выполняются последовательно, поэтому результат предыдущей фазы доступен следующей.
Привязка плагина. Действие выполняется плагином, привязанным к фазе, поэтому замена действия выполняется заменой привязки.
Отдельные жизненные циклы. Сборка, очистка и размещение описываются отдельными циклами, поэтому вызов одного не выполняет другой.
# Фазы выполняются в порядке объявления
mvn -B validate compile test package
# Очистка относится к отдельному циклу
mvn -B clean
# Просмотр привязок плагинов
mvn -B help:describe -Dcmd=package
Практическое следствие: вызов фазы без предшествующей приводит к отсутствию входных данных, поэтому сборка завершается ошибкой на шаге, зависящем от результата предыдущего.
Зависимости и области видимости
Зависимость объявляется координатами, а область видимости определяет доступность на этапах сборки и в итоговом артефакте.
Координаты. Группа, артефакт и версия определяют пакет, поэтому разрешение выполняется однозначно.
Область видимости. Определяет участие в компиляции, тестах и выполнении, поэтому состав артефакта зависит от области.
Транзитивные зависимости. Разрешаются автоматически, поэтому состав дерева определяется объявленными зависимостями.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
# Дерево зависимостей с разрешением конфликтов
mvn -B dependency:tree -Dverbose
Практическое следствие: конфликт версий транзитивных зависимостей разрешается по правилу ближайшего объявления, поэтому фактическая версия может отличаться от ожидаемой.
Локальный репозиторий и разрешение артефактов
Разрешённые артефакты сохраняются в локальном репозитории, поэтому повторная сборка выполняется без загрузки.
Расположение. Определяется настройками, поэтому перенос кэша выполняется изменением пути.
Проверка обновлений. Выполняется по правилам, поэтому снимок и релизная версия обрабатываются по-разному.
Повреждённый артефакт. Приводит к ошибке распаковки, поэтому удаление каталога артефакта восстанавливает загрузку.
# Принудительное обновление снимков
mvn -B -U clean package
# Автономный режим использует только локальный репозиторий
mvn -B -o clean package
# Расположение локального репозитория
mvn -B help:evaluate -Dexpression=settings.localRepository -q -DforceStdout
Практическое следствие: сборка без доступа к сети завершается ошибкой при отсутствии артефакта в локальном репозитории, поэтому автономный режим требует предварительной загрузки.
Связь с настройкой JVM, диагностикой и кэшированием
Результат сборки определяет параметры выполнения, поэтому настройки памяти согласуются с характером нагрузки приложения.
Настройка heap. Параметры запуска задаются отдельно от сборки, поэтому размер области соответствует объёму данных. О выборе границ и целевой паузы — в статье про heap и настройку сборщика мусора.
Диагностика отказа. Завершение по нехватке памяти разбирается по содержимому heap, поэтому источник удержания определяется по дампу. О снятии и разборе дампа — в статье про диагностику JVM и дамп heap.
Кэширование в конвейере. Артефакты сохраняются между запусками, поэтому повторная сборка выполняется быстрее. О ключах кэша — в статье про кеширование и сборку в CI.
Воспроизводимость версий. Фиксация версий в описании проекта обеспечивает одинаковый состав зависимостей, поэтому расхождение между сборками исключено.
Согласование с конвейером. Набор свойств локальной сборки совпадает с конвейерным, поэтому артефакт идентичен.
Проверка артефакта. Состав проверяется после сборки, поэтому отсутствие ресурса обнаруживается до публикации.
Диагностика сборки по выводу фаз
Вывод фазы содержит сведения о выполненном действии и его результате, поэтому причина остановки определяется по записи.
Остановка на фазе. Сообщается номером строки и описанием, поэтому место ошибки определено явно.
Пропущенная фаза. Видна по составу выполненных шагов, поэтому отсутствие входных данных обнаруживается.
Предупреждение о конфликте версий. Сообщается при разрешении дерева, поэтому замещение версии обнаруживается до выполнения.
Журнал загрузки артефактов. Показывает источник получения, поэтому повреждённый артефакт определяется по записи.
Сравнение локальной и конвейерной сборки. Набор выполненных фаз сопоставляется, поэтому различие обнаруживается явно.
Практика: сборка Java-проекта через Maven
- Явная версия JDK. Версия задаётся в описании проекта, поэтому компиляция не зависит от окружения.
- Фиксация версий зависимостей. Версии объявлены явно, поэтому сборка воспроизводима.
- Область видимости по назначению. Тестовые зависимости отделены, поэтому состав артефакта не содержит лишнего.
- Проверка дерева зависимостей. Выполняется при обновлении, поэтому конфликт версий обнаруживается явно.
- Неинтерактивный режим в CI. Ключ отключает запросы, поэтому сборка не останавливается на ожидании ввода.
- Очистка перед сборкой артефакта. Каталог результата очищается, поэтому прежние файлы не попадают в артефакт.
- Разделение тестов и сборки. Пропуск тестов задаётся явно, поэтому ускорение не выполняется неявно.
Рекомендации. Явная версия JDK обеспечивает воспроизводимость. Фиксация версий исключает расхождение. Область видимости ограничивает состав артефакта. Проверка дерева обнаруживает конфликты. Неинтерактивный режим предотвращает остановку сборки.
Практический сценарий: сборка использовала иную версию библиотеки, чем объявлено
Диагностика. Приложение завершалось ошибкой о несуществующем методе. Проверка показала, что фактическая версия библиотеки отличалась от объявленной в описании проекта.
Причина. Транзитивная зависимость объявляла иную версию, которая разрешалась по правилу ближайшего объявления и замещала ожидаемую.
Действия.
# Определение источника версии
mvn -B dependency:tree -Dverbose -Dincludes=com.example:lib
# Фиксация версии через управление зависимостями
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>lib</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
</dependencyManagement>
Результат. Версия зафиксирована управлением зависимостей, поэтому дерево разрешается к ожидаемой версии и ошибка о несуществующем методе устранена.
Вывод. Транзитивные зависимости разрешаются по правилу ближайшего объявления, поэтому фактическая версия фиксируется управлением зависимостей, а не только прямым объявлением.
FAQ
Что выполняет вызов фазы package?
Все предшествующие фазы в порядке объявления, поэтому компиляция и тесты выполняются до создания артефакта.
Чем область видимости test отличается от compile?
Тестовая область доступна только на этапе тестов, поэтому зависимость не попадает в состав итогового артефакта.
Как узнать фактическую версию транзитивной зависимости? Командой построения дерева зависимостей с подробным выводом, поэтому источник версии виден явно.
Почему сборка без сети завершается ошибкой? Отсутствующий в локальном репозитории артефакт не может быть загружен, поэтому автономный режим требует предварительной загрузки.
Источники: Maven: жизненный цикл, Maven: механизм зависимостей, Maven POM Reference.