1. Обзор
В этом руководстве мы рассмотрим два важных тега Maven — dependencyManagement
и dependencies
.
Эти функции особенно полезны для многомодульных проектов.
Мы рассмотрим сходства и различия двух тегов, а также рассмотрим некоторые распространенные ошибки, допускаемые разработчиками при их использовании, которые могут вызвать путаницу.
2. Использование
Как правило, мы используем тег dependencyManagement
, чтобы избежать повторения тегов версии
и области действия
, когда мы определяем наши зависимости в теге зависимостей .
Таким образом, требуемая зависимость объявляется в центральном файле POM.
2.1. Управление зависимостями
Этот тег состоит из тега зависимостей
, который может содержать несколько тегов зависимостей .
Каждая зависимость
должна иметь как минимум три основных тега: groupId ,
ArtifactId и
version
. Давайте посмотрим пример:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
</dependencyManagement>
Приведенный выше код просто объявляет новый артефакт commons-lang3
, но на самом деле он не добавляет его в список ресурсов зависимостей проекта.
2.2. зависимости
Этот тег содержит список тегов зависимостей
. Каждая зависимость
должна иметь как минимум два основных тега: groupId
и ArtiftId .
Давайте посмотрим на быстрый пример:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
Теги версии
и области действия
могут наследоваться неявно, если мы использовали тег dependencyManagement
ранее в файле POM :
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
3. Сходства
Оба этих тега предназначены для объявления какой-либо сторонней зависимости или зависимости от подмодуля. Они дополняют друг друга.
На самом деле мы обычно определяем тег dependencyManagement
один раз, перед тегом зависимостей
. Это используется для объявления зависимостей в файле POM. Это объявление является просто объявлением, и на самом деле оно не добавляет зависимости в проект.
Давайте посмотрим на пример добавления зависимости библиотеки JUnit:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
Как мы видим в приведенном выше коде, есть тег dependencyManagement
, который сам содержит другой тег зависимостей .
Теперь давайте посмотрим на другую сторону кода, которая добавляет фактическую зависимость в проект:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
Итак, текущий тег очень похож на предыдущий. Оба они определяют список зависимостей. Конечно, есть небольшие отличия, о которых мы скоро расскажем.
Одни и те же теги groupId
и ArtiftId
повторяются в обоих фрагментах кода, и между ними существует значимая корреляция: оба они относятся к одному и тому же артефакту.
Как мы видим, в нашем более позднем теге зависимости
нет никакого тега версии
. Удивительно, но это корректный синтаксис, и его можно без проблем разобрать и скомпилировать. Причину легко догадаться: будет использоваться версия, объявленная dependencyManagement
.
4. Отличия
4.1. Структурная разница
Как мы уже говорили ранее, основное структурное различие между этими двумя тегами заключается в логике наследования. Мы определяем версию в теге dependencyManagement
, а затем можем использовать указанную версию, не указывая ее в следующем теге зависимостей .
4.2. Поведенческая разница
dependencyManagement
— это просто объявление, и на самом деле оно не добавляет зависимости. Объявленные зависимости
в этом разделе должны быть позже использованы тегом зависимостей .
Это просто тег зависимостей
, который вызывает реальную зависимость. В приведенном выше примере тег dependencyManagement
не добавит библиотеку junit
ни в какую область. Это просто объявление тега будущих зависимостей .
5. Реальный пример
Почти все проекты с открытым исходным кодом на основе Maven используют этот механизм.
Давайте посмотрим на пример из самого проекта Maven . Мы видим зависимость hamcrest-core
, которая существует в проекте Maven. Сначала он объявляется в теге dependencyManagement
, а затем импортируется основным тегом зависимостей
:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
6. Распространенные варианты использования
Очень распространенный вариант использования этой функции — многомодульный проект.
Представьте, что у нас есть большой проект, состоящий из разных модулей. Каждый модуль имеет свои собственные зависимости, и каждый разработчик может использовать разные версии используемых зависимостей. Тогда это может привести к мешанине различных версий артефактов, что также может привести к трудным и трудноразрешимым конфликтам.
Простым решением этой проблемы, безусловно, является использование тега dependencyManagement
в корневом файле POM (обычно называемом «родительским»), а затем использование зависимостей
в дочерних файлах POM (подмодулях) и даже в самом родительском модуле (если применимо). .
Если у нас есть один модуль, есть ли смысл использовать эту функцию или нет? Хотя это очень полезно в многомодульных средах, также может быть эмпирическим правилом следовать ему как лучшей практике даже в одномодульном проекте. Это улучшает читабельность проекта, а также делает его готовым к расширению до многомодульного проекта.
7. Распространенные ошибки
Одной из распространенных ошибок является определение зависимости только в разделе dependencyManagement
и не включение ее в тег зависимостей .
В этом случае мы столкнемся с ошибками компиляции или выполнения, в зависимости от упомянутой области видимости
.
Давайте посмотрим пример:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
...
</dependencies>
</dependencyManagement>
Представьте приведенный выше фрагмент кода POM. Затем предположим, что мы собираемся использовать эту библиотеку в исходном файле подмодуля:
import org.apache.commons.lang3.StringUtils;
public class Main {
public static void main(String[] args) {
StringUtils.isBlank(" ");
}
}
Этот код не будет компилироваться из-за отсутствия библиотеки. Компилятор жалуется на ошибку:
[ERROR] Failed to execute goal compile (default-compile) on project sample-module: Compilation failure
[ERROR] ~/sample-module/src/main/java/com/foreach/Main.java:[3,32] package org.apache.commons.lang3 does not exist
Чтобы избежать этой ошибки, достаточно добавить приведенный ниже тег зависимостей
в файл POM подмодуля:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
8. Заключение
В этом руководстве мы сравнили теги dependencyManagement
и зависимостей Maven.
Затем мы рассмотрели их сходства и различия и увидели, как они работают вместе.
Как обычно, код этих примеров доступен на GitHub .