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

Конфликтная версия библиотеки в проекте java maven

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

Например, если я добавляю две разные зависимости проекта: A и B, которые оба зависят от HTTP-клиента apache, а каждый из них в другой версии, как только загрузчик классов загружает классы HTTP-клиента apache, B будет пытаться используйте их, поскольку они уже загружены загрузчиком классов.

Но байт-код B зависит от другой версии загруженных классов, вызывающих множество проблем при запуске приложения. Общим является метод notnound exception (поскольку версия http-клиента больше не использует конкретный метод).

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

4b9b3361

Ответ 1

Добро пожаловать в адвентистский адский ад, как он нежно известен. Это довольно распространенная проблема по мере роста проектов и внедрения более внешних зависимостей.

Помимо Apache Commons (упомянутый в вашем исходном вопросе), кадры регистрации (log4j, slf4j) являются еще одним частым виновником.

Я согласен с рекомендацией "матов" о том, как разрешать конфликты после их идентификации. С точки зрения ловушки этих конфликтов версий рано, вы также можете использовать плагин maven "enforcer". См. "dependencyConvergence" config. Также см. этот SO сообщение.

Использование плагина forcecer приведет к сбою сборки сразу в конфликте версии, что избавит вас от ручных проверок. Это агрессивная стратегия, но она предотвращает возникновение проблем во время выполнения, вызвавших ваш вопрос/сообщение. Как и во всяком случае, плагин-исполнитель имеет плюсы и минусы. Мы начали использовать его в течение прошлого года, но потом обнаружили, что это может быть благословением и бичем. Многие версии libs/framework обратно совместимы, и поэтому зависимость (прямо или косвенно) от обеих версий 1.2.3 и 1.2.4 часто прекрасна как во время компиляции, так и во время выполнения. Тем не менее, плагин-исполнитель будет отмечать этот конфликт и требует, чтобы вы указали, какую именно версию вы хотите. Предполагая, что количество конфликтов зависимостей невелико, это не требует большой работы. Однако, как только вы вводите большую структуру (например, Spring MVC), она может стать неприятной.

Надеюсь, эта полезная информация.

Ответ 2

Вы можете использовать tree цель плагина зависимостей Maven для отображения всех транзитивных зависимостей в вашем проекте и поиска зависимостей, которые говорят: опущен для конфликта ". 1

mvn dependency:tree -Dverbose
mvn dependency:tree -Dverbose | grep 'omitted for conflict'

Как только вы узнаете, какая зависимость имеет конфликты версий, вы можете использовать параметр includes, чтобы показывать только зависимости, которые приводят к тому, чтобы увидеть, как втягивается конкретная зависимость. Например, проект, в котором разные версии C вытягиваются А и В:

mvn dependency:tree -Dverbose -Dincludes=project-c

[INFO] com.my-company:my-project:jar:1.0-SNAPSHOT
[INFO] +- project-a:project-a:jar:0.1:compile
[INFO] |  \- project-c:project-c:jar:1.0:compile
[INFO] \- project-b:project-b:jar:0.2:compile
[INFO]    \- project-x:project-x:jar:0.1:compile
[INFO]       \- (project-c:project-c:jar:2.0:compile - omitted for conflict)

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

Однако в других случаях может оказаться невозможным найти версию зависимости, которая работает для всех. В этих случаях вам, возможно, придется отступить от версии в одной из основных зависимостей, чтобы использовать версию транзитивной зависимости, которая работает для всех. Например, в приведенном выше примере A 0.1 использует C 1.0 и B 0.2 использует C 2.0. Предположим, что C 1.0 и 2.0 полностью несовместимы. Но, возможно, ваш проект может использовать вместо B 0.1, что зависит от C 1.5, который совместим с C 1.0.

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

Ответ 3

Я хотел бы продлить ответы Тодда и Матта с тем, что вы можете:

  • mvn dependency:tree -Dverbose -Dincludes=project-c

  • Добавьте тег <exclusions/> для всех ваших зависимостей, которые имеют транзитивную зависимость project-c.

  • Или, альтернативно, внутри вашего проекта явным образом определяю project-c как зависимость, чтобы переопределить транзитивные и избежать конфликта. (Это будет отображаться в вашем дереве при использовании `-Dverbose).

В качестве альтернативы, если эти проекты находятся под вашим контролем, вы можете просто обновить версию project-c.

Ответ 4

Вы можете использовать плагин maven-enforcer в своем pom, чтобы принудительно использовать определенные версии транзитивных зависимостей. Это поможет вам предотвратить упущения конфигурации pom при возникновении конфликтов.

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

Конвергенция зависимостей

<project>
...
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>1.4</version>
        <executions>
          <execution>
            <id>enforce</id>
            <configuration>
              <rules>
                <dependencyConvergence/>
              </rules>
            </configuration>
            <goals>
              <goal>enforce</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
  </build>
  ...
</project>

Принудительно использовать версию с помощью скобок:

<dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <scope>compile</scope>
        <version>[1.0.0]</version>
</dependency>