Java Maven pom.xml
Структура Maven pom.xml: координаты GAV, зависимости, свойства и наследование через родительский POM.
pom.xml — это сердце любого Maven-проекта. POM расшифровывается как Project Object Model — единый XML-файл, который объявляет что представляет собой ваш проект (его идентификация), что ему нужно (зависимости) и как его собрать (плагины и конфигурация). Maven читает этот файл, скачивает всё, на что он ссылается, из репозитория и запускает сборку. Там, где стихийный проект разбрасывает эти сведения по shell-скриптам и папке lib/ с вручную скопированными JAR-файлами, Maven собирает всё в один декларативный, версионируемый документ.
Эта глава разбирает структуру POM по частям: минимальный скелет, координаты GAV, именующие каждый артефакт, принцип работы зависимостей и областей видимости (scope), свойства для устранения дублирования версий и наследование через родительский POM. Если вы только начинаете изучать Maven, начните с введения в Maven; для понимания фаз сборки, управляемых POM, смотрите раздел жизненного цикла сборки Maven.
Минимальный POM
Каждый POM — это XML-документ с корневым элементом <project>, соответствующим схеме Maven 4.0.0. Наименьший работоспособный POM объявляет версию модели и координаты — четыре значения, именующие артефакт:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.w3docs</groupId>
<artifactId>shop-api</artifactId>
<version>1.4.0</version>
<packaging>jar</packaging>
</project><modelVersion> всегда равен 4.0.0 — это версия формата POM, а не вашего кода. Оставьте его именно таким.
Координаты: groupId, artifactId, version
Maven-артефакт идентифицируется своими GAV-координатами. Каждая зависимость, которую вы когда-либо добавите, именуется теми же тремя (иногда четырьмя) значениями, поэтому стоит изучить их точно:
| Элемент | Значение | Соглашение |
|---|---|---|
groupId | Организация или пространство имён | Обратный DNS, например com.google.code.gson |
artifactId | Имя проекта внутри группы | Строчные буквы, через дефис, например shop-api |
version | Выпуск данного артефакта | Семантическое, например 1.4.0; -SNAPSHOT для незавершённых сборок |
packaging | Тип выходного файла | jar (по умолчанию), war, pom |
Вместе groupId:artifactId:version глобально уникальны. Именно это Maven использует для поиска JAR в репозитории и именования артефакта, который создаёт ваша сборка. Версия, оканчивающаяся на -SNAPSHOT (например, 1.5.0-SNAPSHOT), — это изменяемая сборка в разработке, которую Maven может перекачать; обычная версия считается неизменяемой.
Объявление зависимостей
Зависимости перечисляются в элементе <dependencies>, каждая в виде блока <dependency> с координатами нужной библиотеки. Maven разрешает их — а также их зависимости, транзитивно — из репозитория (по умолчанию Maven Central):
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>Элемент <scope> управляет тем, когда зависимость находится в classpath. compile (по умолчанию) означает везде; test — только при компиляции и запуске тестов, так что JUnit никогда не попадёт внутрь вашего JAR. Другие области видимости включают provided (предоставляется средой выполнения, например, servlet API) и runtime (нужна для запуска, но не для компиляции, например, JDBC-драйвер). Транзитивное разрешение, конфликты версий и правила областей видимости подробно рассматриваются в разделе зависимости Maven.
Свойства: не повторяйте версии
Элемент <properties> определяет переиспользуемые переменные, на которые ссылаются в других местах с синтаксисом ${name}. Идиома состоит в том, чтобы однажды закрепить версию каждой библиотеки наверху, чтобы обновления происходили в одном месте:
<properties>
<maven.compiler.release>21</maven.compiler.release>
<junit.version>5.10.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>maven.compiler.release — это широко известное свойство, которое указывает плагину компилятора, на какую версию Java ориентироваться — это чище, чем настраивать плагин вручную. Когда Maven читает POM, он интерполирует каждый заполнитель ${...}, подставляя значение свойства до разрешения чего-либо.
Наследование через родительский POM
POM образуют иерархию. Элемент <parent> заставляет проект наследовать координаты, свойства и управление зависимостями из другого POM. Стартовый родитель Spring Boot — классический пример: он фиксирует согласованный набор версий библиотек, так что вы можете опустить <version> у управляемых зависимостей:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
</parent>
<dependencies>
<dependency>
<!-- version omitted: inherited from the parent's dependencyManagement -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>Многомодульная сборка использует тот же механизм в обратном направлении: один агрегирующий POM с <packaging>pom</packaging> перечисляет <modules>, а каждый дочерний называет его как <parent>. Общая конфигурация хранится в одном месте; дочерние POM остаются компактными.
Практический пример: чтение POM так, как это делает Maven
pom.xml — это просто XML, поэтому мы можем разобрать его встроенным DOM API JDK и обойти его структуру точно так же, как Maven, — читая координаты, свойства и каждую зависимость, попутно вручную интерполируя заполнитель ${...}. (Сам Maven — это инструмент сборки, а не библиотека в classpath, но этот пример показывает модель, на которой он работает.)
Что можно вынести из запуска:
modelVersionвыводится как4.0.0— константа, которую несёт каждый POM. Она идентифицирует формат POM, а не ваш проект; Maven отверг бы POM, в котором она отсутствует.- Координаты выводятся как
com.w3docs:shop-api:1.4.0— тройка GAV в каноническом видеgroupId:artifactId:version. Именно эта строка используется Maven для именования артефакта, который создаёт сборка, и каждой зависимости, которую она разрешает. packagingпрочитан какjar— тип выходного файла по умолчанию. Если бы было значениеpom, это был бы агрегирующий/родительский проект, не производящий собственного JAR.- Свойство
junit.versionразрешено в5.10.2, а заполнитель${junit.version}в зависимости JUnit интерполирован в то же самое значение — именно такая подстановка выполняется Maven, чтобы версия объявлялась в одном месте и переиспользовалась. - Обход зависимостей сообщил о двух зависимостях и вывел каждую с её областью видимости: Gson принял значение по умолчанию
compile(нет элемента<scope>, поэтому она присутствует в каждом classpath), тогда как JUnit показалscope=test, не попадая в поставляемый артефакт.