Maven ·
‹ Предыдущий Следующий ›
⏱ 5 минут чтения Обновлено: 2026-11-07

Многомодульный проект 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 не допускает.

Практика по теме

Закрепите урок на задачах — у каждой есть уровень и оценка времени.

Все задачи модуля →

Комментарии

Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.