Подтвердить что ты не робот

Систематический подход с Maven для борьбы с адским иском

Я борюсь с тем, как приближаться к адской зависимости. У меня есть проект Maven-IntelliJ Scala, который использует некоторые aws-sdk. Недавно добавление кинези sdk внесло несовместимые версии Jackson.

Мой вопрос: как я системно подхожу к проблеме Jar hell?

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

Мои попытки на данный момент основаны на проб и ошибок, и я описываю здесь пример Джексона:

  • Во-первых, я вижу, что такое фактическое исключение, в данном случае NoSuchMethodError, для класса объектов ObjectMapper привязки данных Jackson. Затем я просматриваю документы Джексона, чтобы увидеть, когда этот метод был добавлен или удален. Обычно это довольно утомительно, поскольку я вручную проверяю api docs для каждой версии (вопрос 1: есть ли лучший способ?).
  • Затем я использую mvn dependency:tree, чтобы выяснить, какая версия Jackson я фактически использую (вопрос 2: существует ли автоматический способ запроса maven, какая версия баночки используется, а не расчесывание выход дерева?).
  • Наконец, я сравниваю вывод mvn dependency:tree перед добавлением Kinesis SDK, а затем, чтобы обнаружить различия в выходе mvn dependency:tree и, надеюсь, посмотреть, изменилась ли версия Джексона. (вопрос 3: Как maven использует библиотеки в заштрихованных баночках, когда происходит разрешение зависимостей? Как и любой другой?).

Наконец, после сравнения выходов дерева, я пытаюсь добавить последнюю последнюю версию Джексона в POM, чтобы вызвать приоритет в цепочке разрешения зависимостей maven. Если последнее не работает, я добавляю следующий самый последний lib и т.д.

Вся эта процедура невероятно утомительна. Помимо конкретных вопросов, которые я задал, мне также интересно узнать о системных подходах к этой проблеме. Есть ли у кого-нибудь какие-либо ресурсы, которые они используют?

4b9b3361

Ответ 1

Затем я просмотрю документы Джексона, чтобы увидеть, когда этот метод был добавлен или удален. Это обычно довольно утомительно, так как я вручную проверяю api docs для каждой версии (вопрос 1: есть ли лучший способ?)

Для проверки совместимости API (разрыва) существует несколько инструментов, которые автоматически анализируют банки и предоставляют вам правильную информацию. Из этого в столбце "Переполнение стека" есть хорошие подсказки для некоторых удобных инструментов.
JAPICC кажется неплохим.

Затем я использую mvn dependency:tree, чтобы выяснить, какая версия Джексона, которую я фактически использую (вопрос 2: есть ли автоматический способ запросить maven, какая версия jar используется, а не расчесывать выход дерева?)

maven-dependency-tree - это, безусловно, путь, но вы можете отфильтровать с начала области и получить только то, что вы на самом деле ищете, используя includes следующим образом:

mvn dependency:tree -Dincludes=<groupId>

note: вы также можете предоставить дополнительную информацию опции includes в форме groupId:artifactId:type:version или использовать такие подстановочные знаки, как *:artifactId.

Кажется, небольшой намек, но в больших проектах со многими зависимостями, сужающими его выход, очень помогает. Обычно просто groupId должно быть достаточно в качестве фильтра, *:artifactId, вероятно, самый быстрый, хотя если вы ищете определенную зависимость.

Если вас интересует list зависимостей (а не как дерево), также в алфавитном порядке (довольно удобно во многих сценариях), то также может помочь следующее:

mvn dependency:list -Dsort=true -DincludeGroupIds=groupId

Вопрос 3: Как maven использует библиотеки в затененных банках, когда происходит разрешение зависимостей? Как и любой другой?

Затененными банками вы можете означать:

  • живые банки, которые также привносят в кластер. В этом случае они рассматриваются как одна зависимость, одна единица для Maven Dependency Mediation, тогда ее содержимое будет частью пути к классу проекта. В общем, у вас не должно быть жирных банок как часть ваших зависимостей, так как у вас нет контроля над упакованными библиотеками, которые он вносит.
  • банки с затененными (переименованными) пакетами. В этом случае - опять же - нет никакого контроля в отношении Maven Dependency Mediation: это одна единица, одна банка, основанная на ее GAVC (GroupId, ArtifactId, Version, Classifier), которая делает ее уникальной. Его содержимое затем добавляется к пути к классу проекта (в зависимости от зависимостей область, но поскольку его пакет был переименован, у вас могут быть трудности с обработкой с. Кроме того, вы не должны переименовывать пакеты как часть зависимостей вашего проекта (но часто вы не можете этого знать).

Есть ли у кого-нибудь какие-либо ресурсы, которые они используют?

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

  • dependencyManagement определенно является точкой входа в этом разделе: здесь вы можете иметь дело с Maven Dependency Mediation, влиять на его решение относительно транзитивных зависимостей, их версий, их возможностей. Один важный момент: то, что вы добавляете в dependencyManagement, автоматически не добавляется как зависимость. dependencyManagement учитывается только после того, как определенная зависимость проекта (как указано в файле pom.xml или через транзитивные зависимости) имеет соответствие с одной из его записей, иначе оно просто будет проигнорировано. Это важная часть pom.xml, поскольку она помогает управлять зависимостями и их транзитивными графами и почему часто используется в родительских потах: вы хотите обрабатывать только один и централизованно, какую версию, например, log4j вы хотите использовать во всех ваших проектах Maven, вы объявляете его в общем/общем родительском pom и его dependencyManagement, и вы убедитесь, что он будет использоваться как таковой. Централизация означает лучшее управление и лучшее обслуживание.
  • dependency важнодля объявления зависимостей: обычно вы должны объявлять здесь только прямые зависимости, которые вам нужны. Хорошим правилом thump является: объявить здесь как compile (по умолчанию) область действия только тем, что вы фактически используете в качестве оператора import в своем коде (но вам часто нужно идти дальше этого, например, драйвер JDBC, необходимый во время выполнения и никогда ссылка в вашем коде, тогда она будет находиться в области runtime). Также помните: порядок объявления важен: первая заявленная зависимость выигрывает в случае конфликта с транзитивной зависимостью, следовательно, путем повторного объявления зависимой зависимости вы можете эффективно влиять на посредничество зависимости.
  • Не используйте exclusions в зависимостях для обработки транзитивных зависимостей: используйте dependencyManagement и порядок dependencies для этого, если сможете. Злоупотребление exclusions делает техническое обслуживание намного сложнее, используйте его, только если вам действительно нужно. Кроме того, при добавлении exclusions всегда добавляйте комментарий XML, объясняющий, почему: ваши товарищи по команде и/или ваши будущие лица будут оценены.
  • Использовать зависимости scope задумчиво. Используйте область по умолчанию (compile) для того, что вам действительно нужно для компиляции и тестирования (например, loga4j), используйте test только (и только) для того, что используется в тесте (например, junit), provided для того, что уже предусмотрено целевым контейнером (например, servlet-api), используйте область runtime только для того, что вам нужно во время выполнения, но вы никогда не должны компилироваться с ним (например, драйверы JDBC). Не используйте область system, поскольку она будет только подразумевать проблемы (например, она не упакована с вашим последним артефактом).
  • Не играйте с диапазонами версий, если только по конкретным причинам и не знаете, что указанная версия минимальные требования по умолчанию, выражение [<version>] является самым сильным, но вам редко это нужно.
  • использовать Maven property в качестве заполнителя для2 → элементов семейств библиотек, чтобы убедиться, что у вас есть одно централизованное место для управления версиями набора зависимостей, которые будут иметь все такое же значение версии. Классическим примером может быть свойство spring.version или hibernate.version для использования для нескольких зависимостей. Опять же, централизация означает лучшее управление и поддержание, что также означает меньшую головную боль и меньше ада.
  • При условии, импортировать спецификацию в качестве альтернативы вышеприведенной точке и лучше обрабатывать семейства зависимостей ( например jboss), делегируя другому файлу pom.xml управление определенным набором зависимостей.
  • Не использовать (ab) SNAPSHOT зависимости (или как можно меньше). Если вам действительно нужно, убедитесь, что вы никогда не освобождаетесь, используя SNAPSHOT зависимость: встроенная воспроизводимость будет в большой опасности.
  • При устранении неполадок всегда проверяйте полную иерархию вашего файла pom.xml, используя help:effective-pom. быть действительно полезным при проверке эффективных dependencyManagement, dependencies и properties в отношении конечного графика зависимостей.
  • Используйте другие плагины Maven, чтобы помочь вам в управлении. maven-dependency-plugin действительно полезен при устранении неполадок, но также помогает maven-enforcer-plugin. Вот несколько примеров, заслуживающих упоминания:

В следующем примере убедитесь, что никто (вы, ваша команда и ваше будущее сами) не сможете добавить известную тестовую библиотеку в область compile: сборка завершится с ошибкой. Это гарантирует, что junit никогда не достигнет PROD (в комплекте с вашим war, например.)

<plugin>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>1.4.1<.version>
    <executions>
        <execution>
            <id>enforce-test-scope</id>
            <phase>validate</phase>
            <goals>
                <goal>enforce</goal>
            </goals>
            <configuration>
                <rules>
                    <bannedDependencies>
                        <excludes>
                            <exclude>junit:junit:*:*:compile</exclude>
                            <exclude>org.mockito:mockito-*:*:*:compile</exclude>
                            <exclude>org.easymock:easymock*:*:*:compile</exclude>
                            <exclude>org.powermock:powermock-*:*:*:compile</exclude>
                            <exclude>org.seleniumhq.selenium:selenium-*:*:*:compile</exclude>
                            <exclude>org.springframework:spring-test:*:*:compile</exclude>
                            <exclude>org.hamcrest:hamcrest-all:*:*:compile</exclude>
                        </excludes>
                        <message>Test dependencies should be in test scope!</message>
                    </bannedDependencies>
                </rules>
                <fail>true</fail>
            </configuration>
        </execution>
    </executions>
</plugin>

Посмотрите на другие стандартные правила, которые предлагает этот плагин: многим может быть полезно сломать сборку в случае неправильных сценариев:

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

Ответ 2

Используйте плагин Maven Helper, чтобы легко разрешить все конфликты, исключив старые версии зависимостей.

Ответ 3

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

Прежде всего, я стараюсь иметь четкую карту структуры проекта, отношения между проектами, и обычно я использую графическое представление зависимости Eclipse, которое говорит мне, например, если зависимость не указана для конфликта с другим. Кроме того, он сообщает вам о разрешенных зависимостях для проекта. Я искренне не использую IntelliJ IDEA, но я считаю, что у него есть аналогичная функция.

Обычно я стараюсь добавить в структуру очень общую зависимость, и я использую функцию <dependencyManagement>, чтобы позаботиться о версии для транзитивных зависимостей и, самое главное, избежать дубликатов в проекте структура.

В этом сообщении Maven - Manage Dependencies вы можете найти хороший учебник об управлении зависимостями.

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

В этом блоге Maven Best Practices вы можете найти:

Раздел управления зависимостями Maven позволяет родительскому pom.xml определять зависимых, которые потенциально повторно используются в дочерних проектах. Эта избегает дублирования; без секции dependencyManagement, каждый дочерний проект должен определить свою собственную зависимость и дублировать версии, области действия и типа зависимостей.

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

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

Ответ 4

Даже заменяя всю банку тем же именем, вы все равно можете иметь несколько классов с таким же полным именем. Я использовал плагин maven shade в одном из моих проектов. Он печатает классы с одинаковым квалифицированным именем, исходящие из разных банкоматов. Возможно, это поможет вам