Hands on Maven Dependency Management

Hallo Spaß-Coder.

Im Artikel Mit Maven raus aus der Abhängigkeit haben wir einen Einstieg in die Verwaltung von Abhängigkeiten zwischen Java-Projekten gezeigt. Aber wie genau funktioniert das nun in der Praxis? Wie gehe ich vor, wenn ich Unit Tests in meinem Projekt hinzufügen möchte und dafür JUnit einbinden will? Wo finde ich diese Abhängigkeit für meine POM? Wie macht Maven das mit der Bereitstellung der Abhängigkeiten denn so genau?

Damit Maven genutzt werden kann, muss entweder die IDE-Integration aktiviert oder – so wie wir es im Laufe des Artikels beschreiben – Maven lokal installiert sein. Maven ist für verschiedene Plattformen z.B. unter apache.maven.org zu finden. Unter http://www.it-adviser.net/apache-maven-installieren/ findet ihr eine leicht verständliche Beschreibung der Installation zu Maven auf deutsch. Auf der Kommando-Zeile kann eine erfolgreiche Installation mit dem Befehl mvn -version überprüft werden.

Dabei muss die Version von Maven ausgegeben werden (hier: 3.1.1). Damit Maven erfolgreich betrieben werden kann, muss auch das JDK von Java installiert sein. Das habt ihr aber wahrscheinlich sowieso schon, ihr wollt ja schließlich Java programmieren 😉

Im Folgenden gehen wir davon aus, dass ihr das Grundverständnis aus dem vorangegangenen Artikel mitbringt. Falls euch was unklar ist, lest dort noch einmal nach.

 

Unser kleines Projekt

Beginnen wir damit, ein neues Java-Projekt anzulegen. Diesmal allerdings nicht in unserer Lieblings-IDE, sondern als Project Object Model – also POM. Wir beschreiben damit, was am Ende als Ergebnis herauskommen soll. Dies sieht im ersten Schritt wie folgt aus:

Wie erinnern uns an die Beschreibung unseres Moduls als GAV, also mit der groupId, artifactId und version. Unser Projekt soll den Namen HelloWorld – was auch sonst 😉 – haben und zunächst in der Version 1.0.0 als Schnappschuss – also als Version die sich noch in der Entwicklung befindet – veröffentlicht werden.

Was passiert nun, wenn wir Maven unser Modul bauen lassen? Probieren wir es aus und rufen dazu auf der Kommando-Zeile mvn package im unserem Projektverzeichnis in dem die pom.xml liegt auf. Maven ist so freundlich und kompiliert unseren Code – in diesem Fall haben wir noch keine Programmquellen -, führt alle Unit Tests aus und erstellt die Datei HelloWorld-1.0.0-SNAPSHOT.jar im Unterordner target.

 

Unser Projekt macht auch was

Legen wir eine typische Java-Klasse mit dem Namen HelloWorld.java und diesem Inhalt an:

Die Ausgabe auf dem Standardausgabestrom soll also „Hello Maven-World“ sein. Aber wo legen wir die Datei zu unserer Klasse ab?

Maven arbeitet an vielen Stellen nach dem Prinzip „Convention over Configuration“. So auch bei den Verzeichnissen, in denen unsere Quelldateien abgelegt werden. Hier sieht Maven einen Standard für die Verzeichnisstruktur vor, an den sich alle Maven 3 Projekte halten sollten. Dieser sieht folgendermaßen aus:

  • src
    • main
      • java
        • [packages]
          • [Klassen]
  • test
    • java
      • [packages]
        • [Test-Klassen]

Unsere obige Quelldatei legen wir demnach also im Verzeichnis /src/main/java/de/invidit/ unter dem Namen HelloWorld.java an.

Was passiert nun, wenn wir mit Maven erneut unser Projekt bauen? Rufen wir mvn package also nochmal auf. Hmmm…die Ausgabe sieht genauso aus wir vorher, aber nun ist unser Code in der JAR-Datei enthalten, was wir überprüfen können, in dem wir unsere main()-Methode einfach mal aufrufen (dazu müssen wir zuvor in das target-Verzeichnis wechseln):

Großartig! Aber das ist nur der Anfang. Bis hierhin bietet uns Maven noch keine Vorteile gegenüber unserer IDE.

 

Vor dem Refaktorisieren schreiben wir natürlich Tests

Die Ausgabe auf dem Standardausgabestrom ist nicht sonderlich elegant. Wir ersetzen diese lieber durch eine Ausgabe  in eine Log-Datei. Aber – bevor wir die Änderung an unserem Code vornehmen, sichern wir diese durch Unit Tests ab. Dazu nutzen wir selbstverständlich die bereits vorgestellten Bibliotheken JUnit und AsssertJ.

Nach der Verzeichnisstruktur von oben liegt unsere Klasse HelloWorldTest.java im Verzeichnis /src/test/java/de/invidit/. Maven unterscheidet in der Struktur bereits zwischen Produktivem Quellcode und Testcode.

Um unser Projekt zu bauen, rufen wir erneut was auf? Genau: mvn package. Auf Grund der Standardverzeichnisstruktur von Maven wird unser Unit Test nach dem Kompilieren ausgeführt.

WTF!? Maven kommt gar nicht erst zu unserem Test, da bereits das Kompilieren abbricht. Den Fehlermeldungen können wir entnehmen, dass sowohl JUnit als auch AssertJ nicht gefunden werden. Wo bekommen wir diese jetzt her?

Suchen wir Bibliotheken, die wir mit Maven in unser Projekt einbinden wollen, können wir die Suche unter search.maven.org nutzen und dort einfach unsere Bibliothek suchen. Wer sich den Link nicht merken oder speichern möchte, setzt bei der Suche in der bevorzugten Suchmaschine „maven“ vorneweg und bekommt in der Regel einen guten Treffer. Der Vorteil bei diesen Verzeichnissen ist, dass der Eintrag für unsere POM direkt kopiert werden kann, wenn ich eine Version der Bibliothek ausgewählt habe. Nehmen wir JUnit und AssertJ als Abhängigkeit auf, sieht unsere POM so aus:

Schauen wir mal, ob Maven nun in der Lage ist, unseren Code zu kompilieren und den Test auszuführen. Erneut rufen wir mvn package auf.

YEAH! Am Ende unseres erneuten Aufrufs erhalten wir nun die gewünschte Information, das unser Test ausgeführt wurde.

Der Test war erfolgreich;

  • 1 Test wurde ausgeführt (Tests run)
  • 0 Tests waren fehlerhaft (Failures und Errors)
  • 0 Tests wurden übersprungen (Skipped).

Nach dem Punkt „Results :“ sehen wir noch einmal eine Zusammenfassung aller Tests aus unserem Modul. Da wir derzeit nur genau einen Test haben, unterscheiden sich die beiden Ausgaben nicht.

Wie aber hat Maven das nun gemacht? Schauen wir ein wenig weiter oben in die Ausgabe, sehen wir die Lösung dieses Rätsels:

Maven lädt also die von uns definierten Abhängigkeiten ohne weiteres Zutun herunter. Woher Maven das bekommt (in diesem Fall htp://repo1.maven.org) ist wieder eine Konvention von Maven.

Wenn wir uns das Ganze ein wenig genauer anschauen, finden wir allerdings auch etwas Verwirrendes. Was ist denn hamcrest-core? Das haben wir doch gar nirgendwo hingeschrieben!?! Wo kommt das her?

Dies ist eine weitere Stärke von Maven. Die von uns verwendete Bibliothek JUnit hat selbst eine weitere Abhängigkeit, nämlich hamcrest-core. Dass das so ist, wussten wir bis eben gar nicht – und es muss uns auch nicht weiter interessieren. Maven sorgt dafür, dass wir alle weiteren Abhängigkeiten bekommen, die von den von uns genutzten Bibliotheken benötigt werden.  Abhängigkeiten dieser Art werden transitive Abhängigkeiten genannt.

 

Eine kleine Übung zum Abschluss

Nachdem wir nun die Grundlagen gemeinsam durchgegangen sind und einen Test zur Absicherung der Funktionalität unserer Klasse geschrieben haben, gibt es zum Abschluss nun eine kleine Aufgabe.

Was müssen wir in der POM anpassen, um den Code erfolgreich packen zu können (mvn package), wenn wir den Code unserer Klasse folgendermaßen ändern:

Viel Spaß beim tüfteln.

 

Zusammenfassung und Ausblick

Wir hoffen, dass ihr durch das Selbst-Hand-Anlegen ein wenig mehr Gefühl für den Umgang mit Maven bekommen habt und sich ein bischen die Magie von Maven entfaltet hat.

In den kommenden Artikeln zur Serie werden wir noch ein paar weitere Geheimnisse lüften, etwa wohin Maven die Abhängigkeiten den herunterlädt, was dieses „package“ was wir verwendet haben zu bedeuten hat und wie wir in komplexeren Projekten unsere Abhängigkeiten effektiv verwalten können.

 

Habt ihre Fragen oder Anregungen, sprecht uns gerne in den Kommentaren an. Auch wenn ihr Probleme mit den Anweisungen oder der Abschlussübung haben solltet, könnt ihr gerne dort nachfragen.

Eure Spaß-Coder.

 

Dieser Artikel basiert neben unseren Erfahrungen auf den Ausführungen aus:

  • http://maven.apache.org/
  • http://search.maven.org
  • http://mvnrepository.com

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.