Многомодульный проект Maven: наследование, агрегация и reactor
В проекте интернет-магазина три модуля, и shop-web зависит от shop-service. Разработчик собирает только веб-модуль: cd shop-web, затем mvn package. Сборка падает с ошибкой вида Could not resolve dependencies for project com.examclouds:shop-web:jar:1.0-SNAPSHOT: ... Could not find artifact com.examclouds:shop-service:jar:1.0-SNAPSHOT, хотя каталог shop-service лежит рядом, а из корня та же команда проходит. Из подкаталога Maven видит только один модуль, а соседний ищет как обычную библиотеку в локальном репозитории ~/.m2.
Многомодульный проект Maven (multi-module project) — это проект, разделённый на несколько модулей, у каждого из которых свой pom.xml и свой артефакт, а собираются они вместе одной командой из корневого POM. Его устройство держится на двух независимых механизмах: наследовании (модуль указывает родителя в <parent> и получает от него общие настройки) и агрегации (корневой POM перечисляет модули в <modules>, и Maven собирает их за один запуск). Механизм Maven, который упорядочивает модули по зависимостям и собирает их в одной сборке, называется reactor. Примеры рассчитаны на Maven 3.9.x и Java 25.
Зачем делить проект на модули и как выглядит структура
Проект делят на модули, чтобы разделить код по слоям или функциям с явными границами: модуль видит только те модули, которые объявлены у него в зависимостях. Каждый модуль собирается в собственный артефакт (jar, war), его можно тестировать и переиспользовать отдельно, а общие версии и настройки хранятся в одном родительском POM.
Типичный пример — интернет-магазин из трёх модулей под общим родителем shop-parent:
shop-model— классы предметной области (Product,Order), без зависимостей от других модулей;shop-service— бизнес-логика, зависит отshop-model;shop-web— веб-слой или точка входа приложения, зависит отshop-service(а через него транзитивно и отshop-model).
Такое деление не даёт веб-слою обращаться к базе в обход сервисов, если зависимость на модуль доступа к данным не объявлена: компилятор отклонит такой код ещё при сборке. Структура каталогов:
shop-parent/
+-- pom.xml # parent + aggregator, packaging pom
+-- shop-model/
| +-- pom.xml
| \-- src/main/java/com/examclouds/shop/model/Product.java
+-- shop-service/
| +-- pom.xml
| \-- src/main/java/com/examclouds/shop/service/OrderService.java
\-- shop-web/
+-- pom.xml
\-- src/main/java/com/examclouds/shop/web/App.java Внутри каждого модуля действует обычная структура Maven-проекта. У корневого проекта кода нет: его pom.xml только связывает модули и хранит общие настройки, а результаты сборки появляются в target каждого модуля (shop-model/target/shop-model-1.0-SNAPSHOT.jar и т. д.).
Модули имеют смысл, когда у частей проекта действительно разные зависимости, разные потребители или нужна независимая сборка. Учебный проект на десяток классов делить не нужно: модули добавляют POM-файлы и время сборки, но не дают выигрыша.
Наследование: parent POM
Наследование в Maven означает, что модуль указывает в элементе <parent> координаты родительского POM и получает его настройки: groupId, version, свойства, dependencyManagement, pluginManagement, зависимости и плагины сборки. У родителя должен быть <packaging>pom</packaging>, а artifactId каждый модуль задаёт сам. Общие сведения о parent и Super POM — в разделе parent, Super POM и effective POM.
Родительский shop-parent/pom.xml (он же агрегатор, о нём — в следующем разделе):
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.examclouds</groupId>
<artifactId>shop-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- required for a parent and for an aggregator -->
<packaging>pom</packaging>
<modules>
<module>shop-model</module>
<module>shop-service</module>
<module>shop-web</module>
</modules>
<properties>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- versions of sibling modules: declared once -->
<dependency>
<groupId>com.examclouds</groupId>
<artifactId>shop-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.examclouds</groupId>
<artifactId>shop-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.11.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- inherited by every module -->
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<!-- check the latest versions on Maven Central -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project> POM модуля shop-service/pom.xml получается коротким (атрибуты пространства имён у <project> — как у родителя): groupId, version, версия Java, кодировка, JUnit и версии плагинов приходят от родителя.
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.examclouds</groupId>
<artifactId>shop-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- relativePath defaults to ../pom.xml -->
</parent>
<!-- groupId and version are inherited, artifactId is not -->
<artifactId>shop-service</artifactId>
<dependencies>
<dependency>
<groupId>com.examclouds</groupId>
<artifactId>shop-model</artifactId>
<!-- version comes from dependencyManagement of the parent -->
</dependency>
</dependencies>
</project> Что наследуется, а что нет
| Элемент родителя | Наследуется? | Что получает модуль |
|---|---|---|
groupId, version | Да | Можно не писать, если совпадают с родительскими (обычный случай) |
properties | Да | Свойства родителя; свойство с тем же именем в модуле переопределяет родительское |
dependencyManagement, build/pluginManagement | Да | Управляемые версии и настройки; сами зависимости и плагины не подключаются |
dependencies | Да | Все зависимости родителя подключаются в каждый модуль, нужны они ему или нет |
build/plugins | Да | Плагины и их executions; execution с тем же id в модуле объединяется с родительским |
repositories, distributionManagement, description, url, licenses, developers, scm | Да | Общие метаданные и адреса репозиториев |
artifactId | Нет | Каждый модуль обязан указать свой |
name, prerequisites | Нет (по документации Maven) | Без своего name в логе модуля выводится artifactId |
packaging, modules | Нет | Модуль без своего packaging получает jar, а не pom родителя; список модулей действует только в том POM, где записан |
Отсюда практическое правило: в <dependencies> родителя кладут только то, что нужно действительно всем модулям (тестовые библиотеки, иногда API логирования). Всё остальное — в dependencyManagement, а модули объявляют нужное сами, без версии. Итоговую модель модуля показывает mvn help:effective-pom, запущенный в его каталоге.
relativePath и поиск родителя
Maven 3 ищет родителя сначала по пути <relativePath> (по умолчанию ../pom.xml), проверяя, что у найденного файла те же groupId, artifactId и version, что в <parent>; если файла нет или координаты не совпадают — в локальном и удалённых репозиториях. При стандартной структуре relativePath писать не нужно. Если родитель лежит в другом месте, укажите путь явно; пустой <relativePath/> означает «искать только в репозиториях» — так подключают внешних родителей вроде spring-boot-starter-parent.
<parent>
<groupId>com.examclouds</groupId>
<artifactId>shop-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- parent is two levels up, for example in a nested module -->
<relativePath>../../pom.xml</relativePath>
</parent> Если родителя не нашли ни по пути, ни в репозиториях, сборка останавливается на чтении модели с ошибкой Non-resolvable parent POM (см. таблицу ошибок). Если у родителя не pom, а jar, Maven откажется его использовать:
[ERROR] Invalid packaging for parent POM com.examclouds:shop-parent:1.0-SNAPSHOT, must be "pom" but is "jar" Агрегация: modules и reactor
Агрегация в Maven — это перечисление модулей в элементе <modules> корневого POM, чтобы команда, запущенная в корне, выполнилась для всех модулей за одну сборку. Каждый <module> — путь к каталогу модуля относительно агрегатора (не artifactId). Порядок сборки определяет reactor по зависимостям между модулями, а не порядок строк в <modules>.
Reactor — часть Maven, которая собирает модули из <modules> (рекурсивно, включая вложенные агрегаторы), строит граф связей между ними и сортирует модули так, чтобы каждый собирался после тех, от которых зависит. В граф входят зависимости, родительские POM, а также плагины и расширения из соседних модулей. Внутри одной сборки reactor передаёт модулю результаты соседей напрямую, без установки в ~/.m2.
Проверим на примере: в агрегаторе модули перечислены «задом наперёд».
<modules>
<module>shop-web</module>
<module>shop-service</module>
<module>shop-model</module>
</modules> mvn clean install В начале вывода Maven печатает вычисленный порядок, в конце — итог по модулям. Примерный вид для Maven 3.9.x (оформление зависит от версии; если у модуля задан <name>, выводится он):
[INFO] Reactor Build Order:
[INFO]
[INFO] shop-parent [pom]
[INFO] shop-model [jar]
[INFO] shop-service [jar]
[INFO] shop-web [jar]
...
[INFO] Reactor Summary for shop-parent 1.0-SNAPSHOT:
[INFO]
[INFO] shop-parent ........................................ SUCCESS [ 0.210 s]
[INFO] shop-model ......................................... SUCCESS [ 1.874 s]
[INFO] shop-service ....................................... SUCCESS [ 1.102 s]
[INFO] shop-web ........................................... SUCCESS [ 0.951 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS Несмотря на обратный порядок в <modules>, reactor построил shop-model → shop-service → shop-web, как требуют зависимости; родитель идёт первым, потому что от него зависят все. Порядок из <modules> учитывается только для модулей, не зависящих друг от друга. Если модуль упал, в Reactor Summary он помечен FAILURE, а модули после него — SKIPPED.
Элемент <modules> допустим только в POM с packaging = pom. Если оставить у агрегатора jar (значение по умолчанию), сборка не начнётся:
[ERROR] 'packaging' with value 'jar' is invalid. Aggregator projects require 'pom' as packaging. Модули могут быть вложенными: модуль сам может быть агрегатором со своим <modules> (например, shop-web с подмодулями shop-web-api и shop-web-ui), и reactor соберёт всё дерево из корня.
Состав reactor можно менять и по условию: элемент <modules> внутри профиля добавляет модуль только при активации профиля — например, медленные сквозные тесты shop-e2e-tests собираются лишь командой mvn verify -P e2e. Профили, в том числе объявленные в родительском POM, переключают и свойства, зависимости, плагины; как их объявлять и активировать, разобрано в уроке Профили Maven.
Наследование и агрегация: в чём разница
Наследование и агрегация в Maven решают разные задачи и направлены в разные стороны: при наследовании модуль ссылается на родителя (<parent>), чтобы получить общие настройки, а при агрегации корневой POM ссылается на модули (<modules>), чтобы собрать их одной командой. Чаще всего обе роли выполняет один и тот же корневой POM, но это не обязательно.
| Признак | Наследование (parent) | Агрегация (modules) |
|---|---|---|
| Где объявляется | В модуле: <parent> с GAV родителя | В агрегаторе: <modules> с путями к каталогам |
| Направление ссылки | Модуль → родитель | Агрегатор → модули |
| Что даёт | Общие groupId, version, свойства, управляемые версии, плагины | Сборку всех модулей одной командой в правильном порядке (reactor) |
| Кто знает о связи | Только модуль; родитель о своих наследниках не знает | Только агрегатор; модуль может не знать, что его агрегируют |
| Обязательный packaging | pom у родителя | pom у агрегатора |
| Может ли существовать отдельно | Да: родитель может быть внешним, из репозитория (spring-boot-starter-parent, корпоративный POM) | Да: агрегатор может собирать модули без общего родителя |
Один POM в двух ролях — самый частый вариант, как shop-parent: он и задаёт общие настройки, и перечисляет модули.
Разные POM встречаются, когда настройки и состав сборки живут отдельно. Например, корпоративный родитель com.examclouds:company-parent опубликован в репозитории и используется десятками проектов, а у каждого проекта свой агрегатор. Другой вариант — каталог shop-parent рядом с модулями и агрегатор в корне; тогда модулям нужен <relativePath>../shop-parent/pom.xml</relativePath>.
<!-- aggregator only: no shared settings, no parent required -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.examclouds</groupId>
<artifactId>shop-build</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>shop-parent</module>
<module>shop-model</module>
<module>shop-service</module>
<module>shop-web</module>
</modules>
</project> Зависимости между модулями
Модуль подключает соседний модуль как обычную зависимость — по groupId, artifactId и version. Версию соседа удобнее всего брать из выражения ${project.version} (у всех модулей одна версия) или задать один раз в dependencyManagement родителя, как в примере выше; тогда модули объявляют соседей без версии.
<!-- shop-web/pom.xml, without dependencyManagement in the parent -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>shop-service</artifactId>
<version>${project.version}</version>
</dependency> Жёстко прописанная версия соседа (<version>1.0-SNAPSHOT</version>) работает до первого повышения версии проекта: модули перейдут на 1.1-SNAPSHOT, а зависимость останется на 1.0-SNAPSHOT. Reactor её не узнает и возьмёт старый артефакт из ~/.m2 — или упадёт, если его там нет.
Модули связываются в одну сторону. Если shop-model начнёт зависеть от shop-service, который уже зависит от shop-model, получится цикл, и reactor откажется строить порядок:
[ERROR] The projects in the reactor contain a cyclic reference: Edge between 'Vertex{label='com.examclouds:shop-model:1.0-SNAPSHOT'}' and 'Vertex{label='com.examclouds:shop-service:1.0-SNAPSHOT'}' introduces to cycle in the graph ... Цикл разрывают, вынося общий код в отдельный модуль (например, интерфейсы — в shop-api), от которого зависят оба.
dependencyManagement и pluginManagement в родителе — главный инструмент многомодульного проекта: версии библиотек и плагинов записываются один раз, а модули используют их без номеров. Подробно — в разделах dependencyManagement, BOM и Версии плагинов и pluginManagement.
Команды reactor: -pl, -am, -amd, -rf, -T
Чтобы собрать не весь многомодульный проект, а его часть, команду запускают из корня с ключами reactor: -pl выбирает модули, -am добавляет модули, от которых они зависят, -amd — модули, которые зависят от них, -rf продолжает упавшую сборку с указанного модуля, а -T собирает независимые модули параллельно.
| Ключ | Длинная форма | Что делает | Пример для магазина |
|---|---|---|---|
-pl | --projects | Собирает только перечисленные модули (путь к каталогу или :artifactId, через запятую) | mvn -pl shop-service test — только shop-service |
-am | --also-make | Добавляет модули, от которых зависят выбранные (вверх по графу) | mvn -pl shop-web -am package — parent, model, service, web |
-amd | --also-make-dependents | Добавляет модули, которые зависят от выбранных (вниз по графу) | mvn -pl shop-model -amd verify — model, service, web |
-rf | --resume-from | Начинает сборку с указанного модуля, пропуская те, что стоят раньше в порядке reactor | mvn install -rf :shop-web |
-T | --threads | Параллельная сборка независимых модулей: число потоков или множитель на ядро (1C) | mvn -T 1C verify |
Краткая шпаргалка по этим ключам есть и в разделе Основные команды сборки; здесь — когда какой выбирать.
-pl без -am. mvn -pl shop-web package собирает только shop-web, а shop-service берёт из ~/.m2 — та же ситуация, что в начале урока, только из корня. Если модули, от которых зависит выбранный, менялись, добавляйте -am. Поменяли общий модуль и хотите проверить потребителей — -pl shop-model -amd.
Исключение модуля. Начиная с Maven 3.2.1 модуль можно исключить знаком ! (в bash его нужно взять в кавычки):
mvn -pl '!shop-web' verify -rf после ошибки. Когда модуль падает, Maven сам подсказывает команду продолжения. Примерный вид конца вывода:
[INFO] shop-model ......................................... SUCCESS [ 1.874 s]
[INFO] shop-service ....................................... SUCCESS [ 1.102 s]
[INFO] shop-web ........................................... FAILURE [ 0.412 s]
[INFO] BUILD FAILURE
...
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR] mvn <args> -rf :shop-web При -rf пропущенные модули не пересобираются, и shop-web получит их артефакты из ~/.m2. Это безопасно, если шла команда install и вы меняли только упавший модуль; после verify или правок в предыдущих модулях надёжнее пересобрать с -am.
-T: параллельная сборка. mvn -T 4 install собирает в четыре потока модули, не зависящие друг от друга (у цепочки model → service → web выигрыша почти не будет, у широкого графа — заметный). Плагины, не помеченные как потокобезопасные, Maven отмечает предупреждением; при падениях, которые не воспроизводятся без -T, соберите проект последовательно. Логи модулей при этом перемешиваются.
Почему модуль не видит соседа
Reactor работает только в пределах одной сборки: модуль получает соседа «из reactor», если сосед участвует в этой же сборке, иначе ищет его в локальном, затем в удалённых репозиториях. Отсюда три варианта:
- сборка из корня (с
-amили без-pl) — сосед собирается в той же сборке, всё работает; - сборка из подкаталога или
-plбез-am, соседа в~/.m2нет — ошибкаCould not find artifact, как в начале урока; - то же, но в
~/.m2лежит старая установка соседа — сборка проходит со старым кодом (см. заметку ниже).
Из каталога модуля можно указать корневой POM явно: mvn -f ../pom.xml -pl shop-web -am package. Почему для проверки обычно хватает verify, а install нужен только для внешних потребителей, объяснено в разделе Основные команды сборки; где лежит ~/.m2 — в разделе Локальный репозиторий. В Maven 4 работа из подкаталогов меняется; урок описывает Maven 3.9.x.
Опаснее ошибки: сборка проходит со старым кодом
Неделю назад вы выполнили mvn install из корня. Сегодня добавили метод в OrderService, вызвали его из shop-web и собираете только веб-модуль: cd shop-web, mvn package. Maven возьмёт недельный shop-service-1.0-SNAPSHOT.jar из ~/.m2, и компиляция упадёт с cannot find symbol, хотя метод в исходниках есть. Хуже, если изменилась не сигнатура, а логика: сборка пройдёт, а тесты shop-web пойдут против старой версии сервиса. Собирайте модуль из корня с -am.
Версия многомодульного проекта: ${revision} и versions:set
В многомодульном проекте обычно у всех модулей одна версия: она задаётся в родителе, модули её наследуют. Сложность в том, что в Maven 3 версия в блоке <parent> каждого модуля записывается литералом, и при выпуске новой версии её приходится менять во всех POM. Для этого есть команда versions:set и CI-friendly версия ${revision}.
versions:set
Цель versions:set плагина versions-maven-plugin (MojoHaus), запущенная в корне, меняет версию родителя и ссылки <parent><version> в модулях reactor:
mvn versions:set -DnewVersion=1.1.0-SNAPSHOT
# check the result, then remove backup files:
mvn versions:commit
# or roll back:
mvn versions:revert Рядом с каждым POM плагин оставляет копию pom.xml.versionsBackup (отключается ключом -DgenerateBackupPoms=false). Литеральные версии соседей в <dependency> он может и не обновить — ещё одна причина писать там ${project.version}.
CI-friendly версии: ${revision}
Начиная с Maven 3.5.0 версию можно задать свойствами ${revision}, ${sha1} и ${changelist} — единственное исключение из правила «в <parent> только литералы». Версия записывается один раз, а CI-сервер подставляет свою через -D.
<!-- shop-parent/pom.xml -->
<groupId>com.examclouds</groupId>
<artifactId>shop-parent</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<properties>
<revision>1.0-SNAPSHOT</revision>
</properties>
<!-- shop-service/pom.xml -->
<parent>
<groupId>com.examclouds</groupId>
<artifactId>shop-parent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>shop-service</artifactId> mvn clean package # version 1.0-SNAPSHOT
mvn clean deploy -Drevision=1.0.0 # release build on CI Подвох — в установке и публикации: без дополнительных настроек в ~/.m2 и удалённый репозиторий попадают POM с буквальным текстом ${revision}. Внутри reactor это не мешает, но внешний проект, подключивший shop-service, не сможет разрешить его родителя. Поэтому подключают flatten-maven-plugin (MojoHaus): он записывает .flattened-pom.xml с подставленной версией, и устанавливается именно он.
<!-- shop-parent/pom.xml, build/plugins -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<!-- check the latest version on Maven Central -->
<version>1.6.0</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten-clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin> Режим resolveCiFriendliesOnly заменяет только CI-friendly свойства. Файлы .flattened-pom.xml генерируются при сборке и добавляются в .gitignore. Другие режимы — в документации Maven CI Friendly Versions. Значение -Drevision по умолчанию можно хранить и в файле .mvn/maven.config в корне проекта.
Где чаще всего ломается многомодульная сборка
Ошибки многомодульных проектов делятся на две группы: одни останавливают сборку ещё при чтении POM (неверный packaging, цикл, не найден родитель), другие проходят молча и дают сборку со старым кодом соседа. В таблице — симптом, причина и исправление; сообщения сокращены.
| Симптом | Причина | Как исправить |
|---|---|---|
Invalid packaging for parent POM ..., must be "pom" but is "jar" или Aggregator projects require 'pom' as packaging | У родителя или агрегатора действует packaging по умолчанию jar | Добавить <packaging>pom</packaging> в корневой POM |
Из корня собирается только родитель, BUILD SUCCESS за секунду | Нет <modules> или новый модуль не добавлен в список: <parent> не включает модуль в сборку | Перечислить каталог модуля в <modules>; проверить по Reactor Build Order |
The projects in the reactor contain a cyclic reference | Два модуля зависят друг от друга (напрямую или через третий) | Вынести общий код в отдельный модуль |
Non-resolvable parent POM for com.examclouds:shop-web:1.0-SNAPSHOT: ... 'parent.relativePath' points at wrong local POM | Родитель лежит не в ../pom.xml или координаты в <parent> не совпадают (часто — версия после ручной правки) | Указать верный <relativePath>, сверить GAV; для внешнего родителя — пустой <relativePath/> |
'dependencies.dependency.version' for com.examclouds:shop-model:jar is missing | Зависимость на соседа без версии, а в dependencyManagement родителя его нет | Добавить модуль в dependencyManagement с ${project.version} или указать версию в зависимости |
Could not find artifact com.examclouds:shop-service:jar:1.0-SNAPSHOT | Сборка из подкаталога или -pl без -am: соседа нет ни в reactor, ни в ~/.m2 | Собирать из корня: mvn -pl shop-web -am package |
| После повышения версии модуль собирается со старой версией соседа | Версия соседа в <dependency> записана литералом | ${project.version} или dependencyManagement родителя |
Внешний проект не может подключить модуль: не разрешается родитель с версией ${revision} | CI-friendly версия без flatten-maven-plugin | Подключить flatten-maven-plugin в родителе (пример) |
Часто задаваемые вопросы
Чем модули Maven отличаются от модулей Java (module-info.java)?
Это разные механизмы с одинаковым названием. Модуль Maven — единица сборки: отдельный pom.xml и артефакт. Модуль Java (JPMS, с Java 9) — единица инкапсуляции при компиляции и запуске: module-info.java перечисляет экспортируемые пакеты и нужные модули. Их можно сочетать (часто один Maven-модуль = один Java-модуль), но ни один не требует другого.
Как использовать spring-boot-starter-parent, если у проекта уже есть свой parent?
Родитель у POM один, но наследование бывает многоуровневым: shop-parent наследуется от spring-boot-starter-parent, а модули — от shop-parent. Другой вариант — оставить свой родитель и импортировать в его dependencyManagement BOM spring-boot-dependencies (см. BOM); плагины из starter-parent тогда придётся объявить самостоятельно.
Можно ли модулю не указывать parent?
Можно. Агрегатор соберёт любой модуль из <modules>, даже без <parent> или с другим родителем. Такой модуль сам задаёт groupId, version, версию Java и версии плагинов. Различия ролей — в разделе Наследование и агрегация.
Как пропустить один модуль при сборке?
Начиная с Maven 3.2.1 — ключом -pl с восклицательным знаком: mvn -pl '!shop-web' verify. Если модуль нужен лишь иногда, вынесите его из основного <modules> в профиль (см. урок Профили Maven). Модуль, от которого зависят другие участники сборки, можно исключить, только если его артефакт уже есть в ~/.m2.
Куда класть общий код, нужный нескольким модулям?
В отдельный модуль (например, shop-common или shop-api), от которого зависят потребители. В родительский POM код не положить: у него packaging = pom, исходники в нём не компилируются. Общий модуль заодно разрывает циклические зависимости, которые reactor не допускает.
Практика по теме
Закрепите урок на задачах — у каждой есть уровень и оценка времени.
Все задачи модуля →
Комментарии