Управление зависимостями с помощью подключаемого модуля Maven Dependency.

Много лет назад управление зависимостями для Java-приложений было головной болью. Там не было файлов, описывающих, какие зависимости есть в проекте, поэтому вы иногда исправляли одно ClassNotFoundException за другим, гугля, какие библиотеки содержат ваш отсутствующий класс, а затем добавляя одну за другой в путь к вашему классу — было весело…

К счастью, эти дни прошли благодаря таким инструментам сборки, как Apache Maven или Gradle. Но с новыми инструментами приходится решать новые проблемы — или, может быть, те же самые проблемы приходится решать по-другому. 😉

Типы зависимостей

Прежде чем говорить о реальных проблемах с управлением зависимостями, давайте рассмотрим различные типы зависимостей.

Прямые зависимости

Это обычные зависимости, которые вы объявляете в файле pom.xml вашего проекта. Вот пример прямой зависимости от Spring Boot Web:

Транзитивные (косвенные) зависимости

Придерживаясь примера Spring Boot Web, мы уже можем видеть множество транзитивных зависимостей при запуске mvn dependency:tree:

Таким образом, добавление одной прямой зависимости к Spring Boot Web добавляет несколько других (транзитивных) зависимостей к Jackson, Logback и другим библиотекам.

Проблемы с зависимостями

Каждый тип зависимости создает свои проблемы.

Прямые зависимости

Начнем с одного предложения, которое, наверное, все слышали в прошлом.

Я не знаю, для чего это используется, но мы должны сохранить его.

Со временем зависимости обновляются и добавляются, но редко удаляются. В большинстве случаев люди просто забывают их, а спустя месяцы (или даже годы) все слишком боятся их удалять, потому что они могут быть необходимы, и никто на самом деле не знает, для чего они используются.

В итоге накапливается все больше и больше прямых зависимостей, что приводит к:

  • Артефакты большего размера (например, файлы WAR)
  • Более длительные процессы сборки
  • Неточная картина, нарисованная pom.xml
    (люди, конечно, подумают, что все заявленные зависимости действительно нужны)

Транзитивные (косвенные) зависимости

Вы когда-нибудь слышали о троянском коне? Представьте прямую зависимость как лошадь, а транзитивную зависимость как вещь внутри нее. Хорошо, может быть, это слишком резко, но я надеюсь, что вы поняли суть.

Транзитивные зависимости скрыты, и это совершенно нормально, пока вы не начнете использовать их непосредственно в своем коде. Что происходит тогда, так это то, что у вас есть прямая зависимость, которая объявлена ​​​​только как транзитивная зависимость.

Сначала это может быть хорошо, но проблемы (могут) возникнуть, когда обновляется прямая зависимость (которая приводит к используемой транзитивной зависимости).

Тогда может случиться так, что транзитивная зависимость также будет обновлена, даже если это не было целью. Чтобы было немного понятнее, вот части деревьев зависимостей до и после обновления Spring Boot Web с версии 2.0.0 до 2.1.4.

До:

После:

Поскольку не так просто заметить различия на скриншотах, я укажу вам на это: Spring Boot Web 2.0.0 поставляется с Jackson Core 2.9.4, а Spring Boot Web 2.1.4 поставляется с Jackson Core 2.9.8.

Итак, у вас может возникнуть вопрос: в чем проблема?

Представьте, что класс из Jackson Core, который вы использовали в своем коде, был удален в Jackson Core 2.9.8. Затем вы получите ClassNotFoundException для класса Jackson после обновления Spring Boot Web.Разве это не сбивает с толку‽

— Ну, это точно для меня.

Решение проблем

Основываясь на приведенном выше сценарии, нам нужно решить две проблемы:

  • Неиспользуемые прямые зависимости
  • Используемые транзитивные зависимости

К счастью, и то, и другое можно решить с помощью Maven Dependency Plugin.

Базовая конфигурация для этого будет выглядеть так:

При запуске mvn clean install сборка завершится ошибкой со следующим сообщением:

И это именно то, что мы хотели решить: используемые необъявленные и неиспользуемые объявленные зависимости.

— Итак, давайте исправим сборку!

Используется необъявленный

Если вы используете библиотеки, которые не были явно объявлены в ваших зависимостях, у вас есть два варианта.

Удалите использование из кода
Это редко является решением, поскольку обычно зависимости используются преднамеренно. Хотя в некоторых случаях может случиться так, что класс был использован по ошибке. Для меня это часто происходит с StringUtils, потому что IntelliJ предлагает импорт для этого класса, и когда я не смотрю на них, я могу выбрать неправильный (например, org.apache.commons.lang3.StringUtils вместо org.springframework.util.StringUtils).

Добавить прямую зависимость
В большинстве случаев это правильный путь. Просто добавьте прямую зависимость к файлу pom.xml. Поскольку зависимость уже присутствовала транзитивно, внимательно посмотрите на версию, которую вы использовали раньше, чтобы избежать проблем с другими версиями.

Добавить как исключение
Также можно просто игнорировать определенные зависимости, добавив их в качестве исключения в конфигурацию плагина. Для этого могут быть причины, но этот подход всегда следует рассматривать как последний вариант, поскольку он на самом деле не решает проблему.

Неиспользованный Заявленный

Если вы не используете библиотеки, объявленные в ваших зависимостях, можно рассмотреть несколько вариантов.

Удалить зависимость
Это наиболее очевидное и наиболее часто подходящее решение. Возможно, он действительно больше не используется в коде после предыдущего рефакторинга, поэтому нет необходимости его сохранять.

Изменить область
Подключаемый модуль проверяет зависимости времени компиляции, поэтому зависимости времени выполнения всегда будут помечаться как ошибки, если они не объявлены с областью действия runtime (или системой/при условии). Spring Boot Web — хороший пример зависимости во время выполнения, потому что он делает много «волшебства» во время выполнения и может вам не понадобиться во время компиляции.

Добавить как исключение
Как я уже упоминал в отношении используемых необъявленных зависимостей, существует также возможность определить исключение.

Добавление подключаемого модуля зависимостей Maven в проект

Конечно, я был бы рад, если бы эта статья побудила вас использовать этот плагин в своих проектах, так что вот краткое описание того, как это сделать.

Новые проекты

Разве эти проекты не лучшие? Где вы можете начать с чистого листа и делать все то, о чем всегда мечтали? Конечно, так что используйте классный плагин…
Просто включите его с самого начала и действительно исправьте все ошибки, которые появляются. И под «настоящим исправлением» я подразумеваю отказ от использования исключений. 😉

Существующие проекты

Ну вот и пришло. Есть эти старые проекты, которые уже представляют собой беспорядок (зависимость и код), и где никто не знает, почему, какая зависимость, в какой версии вообще была добавлена. Давайте будем честными, это «нормальные» проекты, которых у всех много.

Хороший подход в таких случаях — начать сейчас с более строгого подхода к зависимостям, чтобы, по крайней мере, вы не вводили больше ошибок, чем уже есть.

Для этого просто добавьте плагин в проект и используйте подходы исключения, чтобы сборка снова прошла успешно. Конечно, было бы еще лучше действительно исправить все ошибки, но это может быть очень сложно и неприятно, поэтому начните с прагматичного подхода, если вам нужно.

Пример проекта см. https://github.com/dnsbtchr/maven-dependency-plugin-example.