Как исключить транзитивную зависимость из classpath в Maven?
Что проверяют
Умеете ли вы разбираться с конфликтами зависимостей, а не просто вставлять <exclusion> из первого найденного ответа. Следом почти всегда спрашивают, как вы нашли, откуда пришла зависимость, и что сломается после исключения.
Как отвечать
Сначала диагностика, потом правка:
- найти источник:
mvn dependency:tree -Dincludes=groupId:artifactIdпоказывает, через какую прямую зависимость приехала библиотека. С-Dverboseвидны и версии, которые Maven отбросил при разрешении конфликта; - исключить: внутри нужной
<dependency>добавить<exclusions>с<exclusion>, где указаны толькоgroupIdиartifactId, версия не пишется. Исключение действует на всё поддерево этой зависимости; - маска: начиная с Maven 3.2.1 в
<exclusion>допускается*, чтобы отрезать все транзитивные зависимости разом; - смена версии вместо исключения: если нужна не другая библиотека, а другая версия, правильнее зафиксировать её в
<dependencyManagement>или импортировать BOM, напримерspring-boot-dependencies; - страховка:
maven-enforcer-pluginс правиламиbannedDependenciesиdependencyConvergenceне даст запрещённому артефакту вернуться с новой зависимостью.
Покажите, зачем это нужно на практике: две реализации логирования в classpath (commons-logging рядом с jcl-over-slf4j, logback рядом с другим бэкендом SLF4J), дубли классов разных версий, встроенный Tomcat, когда нужен Jetty.
Напомните и правило разрешения конфликтов «nearest wins»: Maven берёт версию, которая ближе к корню дерева, а при равной глубине ту, что объявлена раньше. Поэтому версия в итоговом classpath часто не та, которую ожидаешь.
Где спотыкаются
Исключают библиотеку, которая реально нужна в рантайме. Компиляция проходит, а приложение падает с NoClassDefFoundError на первом же вызове кода, до которого тесты не дошли.
Исключают артефакт в одной зависимости и не перепроверяют дерево. Он спокойно приезжает через другую, и конфликт остаётся.
Решают конфликт версий исключением там, где нужен <dependencyManagement>. Если нужная версия больше ниоткуда не приходит, класс просто исчезает из classpath.