Несколько версий одной и той же зависимости в Maven

Можно ли объявить несколько версий одной и той же зависимости в репозитории Maven?

Мне нужны эти зависимости сразу:

    <dependency>
        <groupId>org.bukkit</groupId>
        <artifactId>craftbukkit</artifactId>
        <version>1.7.9-R0.2</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.bukkit</groupId>
        <artifactId>craftbukkit</artifactId>
        <version>1.7.2-R0.3</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.bukkit</groupId>
        <artifactId>craftbukkit</artifactId>
        <version>1.6.4-R2.0</version>
        <scope>compile</scope>
    </dependency>

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

org.bukkit.craftbukkit.v1_6_R3

org.bukkit.craftbukkit.v1_7_R1

org.bukkit.craftbukkit.v1_7_R3

Если я объявлю зависимости, как показано в первом фрагменте, вступит в силу только последний. Есть ли способ достичь этого в Maven?

@ Редактировать Любой обходной путь, возможно?

5 ответов

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

Можно указать разные версии зависимостей, используя разные профили. Для каждой версии Bukkit профиль может быть определен и активирован. Тем не менее, если вы активируете более одного профиля, будет использоваться только одна версия.

<profiles>
    <profile>
        <id>Bukkit_1_7_9_R02</id>
        <activation>
            ...
        </activation>
        <dependencies>
            <dependency>
                <groupId>org.bukkit</groupId>
                <artifactId>craftbukkit</artifactId>
                <version>1.7.9-R0.2</version>
                <scope>compile</scope>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>Bukkit_1_7_2_R03</id>
        <activation>
            ...
        </activation>
        <dependencies>
            <dependency>
                <groupId>org.bukkit</groupId>
                <artifactId>craftbukkit</artifactId>
                <version>1.7.2-R0.3</version>
                <scope>compile</scope>
            </dependency>
        </dependencies>
    </profile>
    ...
</profiles>

Попробуй обмануть мавена

<dependency>
    <groupId>org.bukkit</groupId>
    <artifactId>craftbukkit</artifactId>
    <version>1.7.9-R0.2</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.bukkit.</groupId>
    <artifactId>craftbukkit</artifactId>
    <version>1.7.2-R0.3</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.bukkit..</groupId>
    <artifactId>craftbukkit</artifactId>
    <version>1.6.4-R2.0</version>
    <scope>compile</scope>
</dependency>

Вот соответствующая часть документации Maven, в которой объясняется, как Maven выбирает версию зависимости, когда существует более одной возможности:

Посредничество зависимостей - это определяет, какая версия артефакта будет выбрана, когда несколько версий обнаруживаются как зависимости. Maven выбирает "ближайшее определение". То есть он использует версию наиболее близкой к вашему проекту зависимости в дереве зависимостей. Вы всегда можете гарантировать версию, явно указав ее в POM вашего проекта. Обратите внимание: если две версии зависимостей находятся на одной и той же глубине в дереве зависимостей, первое объявление выигрывает. "ближайшее определение" означает, что используемая версия будет самой близкой к вашему проекту в дереве зависимостей. Рассмотрим это дерево зависимостей:

A
├── B
│   └── C
│       └── D 2.0
└── E
    └── D 1.0

В тексте зависимости для A, B и C определены как A -> B -> C -> D 2.0 и A -> E -> D 1.0, тогда D 1.0 будет использоваться при построении A, потому что путь от A до От D до E короче. Вы можете явно добавить зависимость от D 2.0 в A, чтобы принудительно использовать D 2.0, как показано здесь:

A
├── B
│   └── C
│       └── D 2.0
├── E
│   └── D 1.0
│
└── D 2.0      

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

Но это требование иногда действительно - например, когда обслуживание этой библиотеки плохое, и они перемещают много пакетов и объявляют эту младшую версию одного и того же артефакта. Затем разные библиотеки используют это как стороннюю зависимость и требуют разных одинаковых классов в разных FQCN.

В таких случаях вы можете, например, использовать maven-shade-plugin,

  • Создайте проект maven с одной зависимостью, одной из нужных вам версий.
  • Добавить shade Плагин, и пусть он создает затененную банку. Это будет в основном переупаковывать классы под другим артефактом G:A:V.
  • Сделайте это для всех версий, которые вам нужны.
  • Вы можете использовать классификатор, чтобы различать затененные версии.
  • В вашем проекте зависят от этих артефактов.
  • Наконец, исключите исходные зависимости, дайте им область "предоставлено".

Вы можете использовать разные варианты одного и того же, что в итоге поместит эти классы в ваш путь к классам. Например, используйте dependency:copy-dependency плагин / цель, и установите этот jar в локальный репозиторий во время сборки. Или распакуйте классы прямо в свой ${project.build.outputDirectory} (целевые / классы).

Я все еще довольно новичок, но кое-что, с чем я столкнулся с axis2, заключается в том, что отдельным модулям иногда требуется более ранняя версия из-за изменений, которые они внесли в классы, поэтому зависимость верхнего уровня ловит только половину из них. Остальное мне приходилось индивидуально исправлять poms для явных зависимостей в другой версии.

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

Вот сценарий, в котором может возникнуть проблема, когда maven рассматривает только один экземпляр зависимости.

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

Желаемый эффект состоит в том, что конечный проект включает объединенное подмножество, но на самом деле он будет включать только одно подмножество.

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

Вот как я обошел это. К вашему сведению: в моем случае я строил RPM.

Я использовал maven-dependency-plugin скопировать старую зависимость, которая игнорируется, в папку в каталоге сборки, а затем скопировать этот файл в промежуточную область, чтобы включить его в RPM.

Вот код для копирования старой зависимости:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>3.1.1</version>
    <executions>
      <execution>
        <id>copy-dependencies</id>
        <phase>prepare-package</phase>
        <goals>
          <goal>copy</goal>
        </goals>
        <configuration>
          <artifactItems>
            <artifactItem>
              <groupId>some.package.group.id</groupId>
              <artifactId>someartifact-id</artifactId>
              <version>1.2.3</version>
              <overWrite>false</overWrite>
              <outputDirectory>${project.build.directory}/older-dependencies</outputDirectory>
            </artifactItem>
          </artifactItems>
        </configuration>
      </execution>
    </executions>
  </plugin>

А потом, во время сборки моего RPM, я включил этот скриптлет в configuration раздел моего rpm-maven-plugin, Это скопирует файл в промежуточную область для RPM:

<installScriptlet>
  <script>cp ${project.build.directory}/older-dependencies/* ${project.build.directory}/rpm/${artifactId}/buildroot${installBase}/</script>
</installScriptlet>
Другие вопросы по тегам