So lösen Sie eine Versionskollision von Artefakten in Maven

1. Übersicht

Maven-Projekte mit mehreren Modulen können komplexe Abhängigkeitsdiagramme aufweisen. Diese können zu ungewöhnlichen Ergebnissen führen, je mehr die Module voneinander importieren.

In diesem Tutorial erfahren Sie, wie Sie die Versionskollision von Artefakten in Maven beheben können .

Wir beginnen mit einem Projekt mit mehreren Modulen, bei dem wir absichtlich verschiedene Versionen desselben Artefakts verwendet haben. Dann werden wir sehen, wie Sie verhindern können, dass die falsche Version eines Artefakts entweder durch Ausschluss- oder Abhängigkeitsverwaltung abgerufen wird.

Schließlich werden wir versuchen, das Maven-Enforcer-Plugin zu verwenden, um die Kontrolle zu vereinfachen, indem wir die Verwendung von transitiven Abhängigkeiten verbieten.

2. Versionskollision von Artefakten

Jede Abhängigkeit, die wir in unser Projekt aufnehmen, kann mit anderen Artefakten verknüpft sein. Maven kann diese Artefakte, auch transitive Abhängigkeiten genannt, automatisch einbringen. Eine Versionskollision tritt auf, wenn mehrere Abhängigkeiten mit demselben Artefakt verknüpft sind, jedoch unterschiedliche Versionen verwenden.

Infolgedessen können sowohl in der Kompilierungsphase als auch zur Laufzeit Fehler in unseren Anwendungen auftreten .

2.1. Projektstruktur

Definieren wir eine Projektstruktur mit mehreren Modulen, mit der experimentiert werden soll. Unser Projekt besteht aus einem übergeordneten und drei untergeordneten Modulen für Versionskollision :

version-collision project-a project-b project-collision 

Die pom.xml für Projekt-a und Projekt-b sind fast identisch. Der einzige Unterschied ist die Version des Artefakts com.google.guava , von der sie abhängen. Insbesondere verwendet project-a Version 22.0 :

  com.google.guava guava 22.0  

Aber Projekt-b verwendet die neuere Version, 29,0-jre :

  com.google.guava guava 29.0-jre  

Das dritte Modul, Projektkollision , hängt von den beiden anderen ab:

  com.baeldung project-a 0.0.1-SNAPSHOT   com.baeldung project-b 0.0.1-SNAPSHOT  

Also, welche Version von Guave wird für Projektkollisionen verfügbar sein ?

2.2. Verwenden von Funktionen aus der Version für bestimmte Abhängigkeiten

Wir können herausfinden , welche die Abhängigkeit durch die Schaffung eines einfachen Test in der verwendet wird , Projekt-Kollisions - Modul , das die verwendet Futures.immediateVoidFuture Methode aus Guave :

@Test public void whenVersionCollisionDoesNotExist_thenShouldCompile() { assertThat(Futures.immediateVoidFuture(), notNullValue()); }

Diese Methode ist nur ab der Version 29.0 verfügbar . Wir haben dies von einem der anderen Module geerbt, aber wir können unseren Code nur kompilieren, wenn wir die transitive Abhängigkeit von Projekt-b erhalten haben.

2.3. Kompilierungsfehler durch Versionskollision

Abhängig von der Reihenfolge der Abhängigkeiten im Projektkollisionsmodul gibt Maven in bestimmten Kombinationen einen Kompilierungsfehler zurück:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:testCompile (default-testCompile) on project project-collision: Compilation failure [ERROR] /tutorials/maven-all/version-collision/project-collision/src/test/java/com/baeldung/version/collision/VersionCollisionUnitTest.java:[12,27] cannot find symbol [ERROR] symbol: method immediateVoidFuture() [ERROR] location: class com.google.common.util.concurrent.Futures

Dies ist das Ergebnis der Versionskollision des Artefakts com.google.guava . Standardmäßig wählt Maven für Abhängigkeiten auf derselben Ebene in einem Abhängigkeitsbaum die erste Bibliothek aus, die gefunden wird. In unserem Fall befinden sich beide com.google.guava- Abhängigkeiten auf derselben Höhe und die ältere Version wird ausgewählt.

2.4. Verwenden des Maven-Dependency-Plugins

Das Maven-Dependency-Plugin ist ein sehr hilfreiches Tool, um alle Abhängigkeiten und ihre Versionen darzustellen :

% mvn dependency:tree -Dverbose [INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ project-collision --- [INFO] com.baeldung:project-collision:jar:0.0.1-SNAPSHOT [INFO] +- com.baeldung:project-a:jar:0.0.1-SNAPSHOT:compile [INFO] | \- com.google.guava:guava:jar:22.0:compile [INFO] \- com.baeldung:project-b:jar:0.0.1-SNAPSHOT:compile [INFO] \- (com.google.guava:guava:jar:29.0-jre:compile - omitted for conflict with 22.0)

Das Flag -Dverbose zeigt widersprüchliche Artefakte an. Tatsächlich haben wir eine com.google.guava- Abhängigkeit in zwei Versionen: 22.0 und 29.0-jre. Letzteres möchten wir im Projektkollisionsmodul verwenden .

3. Ausschließen einer transitiven Abhängigkeit von einem Artefakt

Eine Möglichkeit, eine Versionskollision zu beheben, besteht darin , eine widersprüchliche transitive Abhängigkeit von bestimmten Artefakten zu entfernen . In unserem Beispiel möchten wir nicht, dass die Bibliothek com.google.guava transitiv aus dem Projekt hinzugefügt wird - ein Artefakt.

Daher können wir es in der Projektkollision pom ausschließen:

  com.baeldung project-a 0.0.1-SNAPSHOT   com.google.guava guava     com.baeldung project-b 0.0.1-SNAPSHOT  

Wenn wir jetzt den Befehl dependency: tree ausführen , können wir sehen, dass er nicht mehr vorhanden ist:

% mvn dependency:tree -Dverbose [INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ project-collision --- [INFO] com.baeldung:project-collision:jar:0.0.1-SNAPSHOT [INFO] \- com.baeldung:project-b:jar:0.0.1-SNAPSHOT:compile [INFO] \- com.google.guava:guava:jar:29.0-jre:compile

Infolgedessen endet die Kompilierungsphase fehlerfrei und wir können die Klassen und Methoden ab Version 29.0-jre verwenden .

4. Verwenden Sie den Abschnitt dependencyManagement

Der Abschnitt dependencyManagement von Maven ist ein Mechanismus zum Zentralisieren von Abhängigkeitsinformationen . Eine der nützlichsten Funktionen ist die Steuerung von Versionen von Artefakten, die als transitive Abhängigkeiten verwendet werden.

In diesem Sinne erstellen wir eine dependencyManagement- Konfiguration in unserem übergeordneten pom :

   com.google.guava guava 29.0-jre   

Infolgedessen stellt Maven sicher, dass in allen untergeordneten Modulen die Version 29.0-jre des Artefakts com.google.guava verwendet wird :

% mvn dependency:tree -Dverbose [INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ project-collision --- [INFO] com.baeldung:project-collision:jar:0.0.1-SNAPSHOT [INFO] +- com.baeldung:project-a:jar:0.0.1-SNAPSHOT:compile [INFO] | \- com.google.guava:guava:jar:29.0-jre:compile (version managed from 22.0) [INFO] \- com.baeldung:project-b:jar:0.0.1-SNAPSHOT:compile [INFO] \- (com.google.guava:guava:jar:29.0-jre:compile - version managed from 22.0; omitted for duplicate)

5. Verhindern Sie versehentliche transitive Abhängigkeiten

The maven-enforcer-plugin provides many built-in rules that simplify the management of a multi-module project. One of them bans the use of classes and methods from transitive dependencies.

Explicit dependency declaration removes the possibility of version collision of artifacts. Let's add the maven-enforcer-plugin with that rule to our parent pom:

 org.apache.maven.plugins maven-enforcer-plugin 3.0.0-M3   enforce-banned-dependencies  enforce         

As a consequence, we must now explicitly declare the com.google.guava artifact in our project-collision module if we want to use it ourselves. We must either specify the version to use, or set up dependencyManagement in the parent pom.xml. This makes our project more mistake proof, but requires us to be more explicit in our pom.xml files.

6. Conclusion

In this article, we've seen how to resolve a version collision of artifacts in Maven.

First, we explored an example of a version collision in a multi-module project.

Anschließend haben wir gezeigt, wie transitive Abhängigkeiten in der Datei pom.xml ausgeschlossen werden können . Wir haben uns angesehen, wie Abhängigkeitsversionen mit dem Abschnitt dependencyManagement in der übergeordneten Datei pom.xml gesteuert werden .

Schließlich haben wir versucht, mit dem Maven-Enforcer-Plugin die Verwendung von transitiven Abhängigkeiten zu verbieten, um jedes Modul zu zwingen, die Kontrolle über sein eigenes zu übernehmen.

Wie immer ist der in diesem Artikel gezeigte Code über GitHub verfügbar.