W3docs

Введение в Java Maven

Что такое Maven, как он собирает проекты Java и как установить и запускать команды mvn.

Введение в Java Maven

Maven — это стандартный инструмент автоматизации сборки и управления зависимостями для Java. Он превращает проект в декларативное описание — что он представляет собой, от чего зависит, как собирается — и затем выполняет сборку за вас. Вместо ручного управления JAR-файлами и флагами javac вы объявляете свои потребности в одном файле, и Maven загружает зависимости, компилирует, тестирует и упаковывает ваш код через предсказуемый, повторяемый жизненный цикл.

POM: проект как данные

Каждый проект Maven описывается файлом pom.xml (Project Object Model). Он идентифицирует проект тремя координатами — groupId, artifactId и version (вместе «GAV») — и перечисляет нужные ему библиотеки. Та же схема координат именует ваш проект и каждую зависимость, и именно так Maven находит артефакты в репозитории.

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>shop-app</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.release>21</maven.compiler.release>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>6.1.0</version>
    </dependency>
  </dependencies>
</project>

Элемент <packaging> сообщает Maven, что производить (jar, war, pom). Блок <properties> хранит переиспользуемые значения — здесь релиз Java, на который нацелен компилятор.

Зависимости и транзитивный classpath

Вы перечисляете только свои прямые зависимости. Maven читает собственный POM каждой зависимости и подтягивает всё, что нужно имтранзитивные зависимости — автоматически выстраивая полный classpath. Объявление spring-web также привносит spring-core, хотя вы его не называли.

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.17.0</version>
</dependency>

У каждой зависимости есть scope (область видимости), который определяет, когда она находится в classpath. Значение по умолчанию compile делает её доступной везде; test ограничивает её тестовым кодом; provided означает, что её предоставляет среда выполнения.

ScopeВ compile-classpathВ test-classpathУпакованаТипичное применение
compile (по умолчанию)ДаДаДаОбычные библиотеки
providedДаДаНетServlet API, предоставляется в среде выполнения
runtimeНетДаДаДрайверы JDBC
testНетДаНетJUnit, Mockito

Жизненный цикл сборки

Сборки Maven проходят через фиксированную последовательность фаз. Ключевые фазы стандартного жизненного цикла — это validate, compile, test, package, verify, install и deploy. Запуск фазы выполняет каждую фазу перед ней — mvn package сначала валидирует, компилирует и тестирует, затем упаковывает. Вы никогда не вызываете фазы вне порядка.

mvn compile          # скомпилировать основные исходники
mvn test             # компиляция + запуск юнит-тестов
mvn package          # компиляция + тесты + сборка JAR/WAR
mvn install          # упаковка + копирование артефакта в ваш локальный репозиторий
mvn clean package    # сначала удалить target/, затем собрать с нуля

Каждая фаза привязана к одной или нескольким целям (goals), предоставляемым плагинами (например, compiler:compile привязана к фазе compile). В плагинах живёт фактическая работа; жизненный цикл лишь упорядочивает её.

Репозитории

Maven получает артефакты из репозиториев. При сборке он сначала смотрит в ваш локальный репозиторий (~/.m2/repository), кэш на вашей машине. При промахе он загружает из удалённого репозитория — по умолчанию Maven Central — и сохраняет копию локально, чтобы следующая сборка была быстрой и офлайн. Команды часто добавляют приватный репозиторий для внутренних библиотек.

pom.xml проекта  →  локальный репо (~/.m2)  →  удалённый репо (Maven Central)
                    (попадание в кэш?)          (загрузка + кэширование)

Запускаемый пример

В исполнителе кода нет Maven в classpath, поэтому программа ниже моделирует основную механику Maven обычным кодом JDK: она хранит крошечный POM как map, разрешает транзитивное замыкание зависимостей (устраняя дубликаты общих артефактов) и проходит по жизненному циклу сборки, останавливаясь на запрошенной фазе. Формы здесь — координаты GAV, транзитивное разрешение, упорядоченные фазы — это в точности то, что делает настоящий Maven.

java— editable, runs on the server

Что вынести из запуска:

  • Проект и каждая зависимость именуются одним и тем же тройным GAV, поэтому Project coordinates: com.example:shop-app:1.0.0 читается ровно как строка зависимости.
  • Объявлены только две зависимости, но разрешённый classpath содержит четыре JAR — spring-core и jackson-annotations были подтянуты транзитивно, ровно как делает Maven.
  • Множество seen гарантирует, что каждый артефакт появляется один раз; это и есть дедупликация Maven, которая не даёт общей библиотеке оказаться в classpath дважды.
  • Executing: mvn package выполняет validate, compile и test перед package и затем останавливается — запуск фазы всегда выполняет каждую предыдущую фазу по порядку.
  • Сборка останавливается на package и никогда не доходит до install, отражая то, как запрошенная цель ограничивает, насколько далеко продвигается жизненный цикл.

Практика

Практика

Что происходит в Maven, когда вы запускаете 'mvn package'?