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.