Управление зависимостями в Maven

Apache — отличная штука, для управления сторонними зависимостями в вашем проекте. Достаточно сказать ему,  какой артефакт вам нужен и всё остальное сделает сам.

Добавление зависимостей

Все зависимости перечисляются в секции <dependencies/>, одна за одной, в любом порядке.

Например, в архетипе quickstart автоматически добавляется библиотека JUnit3. Если мы хотим перейти с JUnit3 на JUnit4, то достаточно изменить версию в зависимости:

Чтобы добавить какую-нибудь другую зависимость, например TestNG, надо знать её maven координаты, то есть группу, артефакт и версию:

Переменные в maven

Перед тем как продолжить добавлять зависимости, нужно сделать шаг к переменным в maven. Maven позволяет задавать переменные (а сам задаёт ещё больше), значения которых впоследствии можно использовать где угодно:

И в тег <version/>  будет подставлено значение «1.2.3».

Те же самые переменные можно и нужно использоват, чтобы задавать версию зависимости отдельно от самой зависимости:

Такое раздельное описание зависимостей имеет два преимущества:

  • В одном списке, который можно окинуть одним взглядом, перечислены зависимости и их версии.
  • Некоторые библиотеки (скажем Spring) состоят из нескольких артефактов с одной и той же версией и, таким образом, проще изменить версию только в одном месте, чем в нескольких

Кстати, подписывать какие зависимости для чего нужны, тоже не лишне.

Области видимости

Вы могли заметить, что описания зависимостей для JUnit и Spring framework отличаются параметром scope, он же — область видимости. Scope позволяет указать maven’у когда и для чего вам нужна эта зависимость. Всего существует шесть областей видимости:

  • compile — область видимости по умолчанию. Зависимости с этим scope будут доступны и во время сборки и во время тестирования и их даже добавят в конечный пакет, чтобы они были доступны и во время исполнения. Более того, maven распространит их дальше и сделает доступными в зависимых пакетах.
  • provided — Почти как compile, но в пакет зависимость добавлена не будет. Предполагается что данные библиотеки будут предоставлены средой выполнения, например J2EE контейнером. Каноничный пример такой зависимости — J2EE API, конкретная реализация которых предоставляется контейнером J2EE.
  • runtime — антипод provided. Означает зависимость, которая требуется для исполнения/тестирования кода, но не для его сборки. Зависимости из этого
    scope так же будут добавлены в пакет.
  • test — зависимости, которые нужны только и исключительно для тестов. Как JUnit из примера выше.
  • system — зависимость которая присутствует в среде Java всегда, тем или иным путём. Maven не будет пытаться предоставить этот артефакт или класть
    его в пакет итд.
  • import — использутся для импорта зависимостей из других артефактов и управлением зависимостями в сложных пакетах, состоящих из нескольких артефактов.

Например, в одном из моих проектов есть такие зависимости:

  • org.projectlombok:lombok — утилита для генерации кода, работающая только во время компиляции. Поэтому у неё scope — provided
  • org.slf4j:slf4j-api — Фронтенд для ведения логов. Мой проект с ним собирается, тестируется и работает. Scope Slf4j — compile.
  • org.slf4j:slf4j-log4j12 — Реализация Slf4j поверх log4j. Мой проект не ссылается напрямую на эту библиотеку и использует её только посредством Slf4j, поэтому её Scope — runtime.
  • junit:junit — Фреймворк юнит-тестирования. Очевидно что он нужен только во время исполнения тестов и из-за этого его scope — test.

Области видимости system и import используются крайне редко.

Исключение зависимостей

Как я уже писал выше, maven весьма удобная штука для управления зависимостями. И достаточно умная, чтобы понять, что у зависимостей бывают  зависимости, а у них свои зависимости и т. д. И если вы добавите в pom.xml скажем, hibernate, то maven самостоятельно добавит ещё antlr, commons-collections, dom4j и так далее.

Но иногда хочется сказать maven, чтобы он так не делал. Положим у вас есть две библиотеки X и Y, причём Y явно зависит от библиотеки X другой версии. Maven предоставит вам и то и другое и у вас случится конфликт. Для этого стоит научится исключать зависимости из зависимостей:

Я знаю, что unitils-dbmaintainer зависит от dbunit предыдущей версии, но тесты моего приложения зависят от более новой версии. Поэтому я явно добавляю зависимость от dbunit нужной мне версии и исключаю старый dbunit из зависимостей unitils-dbmaintainer.

Чтобы просмотреть, какие именно зависимости (с учётом их зависимостей) имеет ваш артефакт, можно попросить maven нарисовать дерево зависимостей:

Или тоже самое, но с подробностями: