Maven

  1. Motivation
  2. Begriffserklärung
    1. Maven
    2. Plugins und Goals
    3. Lifecycles
  3. Zusammenhang der Komponenten
  4. POM
  5. Einrichtung im Projekt
  6. Gradle vs. Maven

1. Motivation

Die Funktionalität des Maven-Build-Systems ist breiter als der Quellcode-Compiler. Wenn eine Anwendung ausgeführt wird, ruft Apache Maven den Compiler auf und verwaltet automatisch Abhängigkeiten und Ressourcen, zum Beispiel:

  • lädt die entsprechenden Paketversionen herunter;
  • platziert Bilder, Audio- und Videodateien in den richtigen Ordnern;
  • lädt Bibliotheken von Drittanbietern.

Das automatische Erstellen einer Anwendung ist besonders wichtig während der Entwicklungs-, Debugging- und Testphasen – Maven hilft dabei, Code und Ressourcen ohne IDE (Entwicklungsumgebung) in eine ausführbare Anwendung zu integrieren. Gleichzeitig ist das Montagesystem flexibel:

  • kann in IDE verwendet werden – Eclipse, IntelliJ IDEA, NetBeans und andere;
  • unabhängig vom Betriebssystem;
  • erfordert keine Installation – das Archiv mit dem Programm kann in ein beliebiges Verzeichnis entpackt werden;
  • alle notwendigen Parameter sind optimal voreingestellt;
  • vereinfacht die Organisation von Teamarbeit und Dokumentation;
  • startet Bibliotheken für Unit-Tests;
  • stellt die Einhaltung von Standards sicher;
  • hat eine große Anzahl von Plugins und Erweiterungen.

2. Begriffserklärung

2.1. Maven

Maven ist ein Build-Tool für die Projektverwaltung, mit dem sich Java-Projekte automatisieren lassen.

Der gesamte Prozess, aus dem Quellcode eine funktionierende App zu machen, wird als Software-Build bezeichnet.

2.2. Plugins und Goals

Plugins sind ein Satz von Zielen (goals). Üblicherweise lassen sich Plugins sehr flexibel konfigurieren. Alle offiziellen Plugins von Maven-Entwicklern sind auf der offiziellen Maven-Website sehr gut dokumentiert. Beispielsweise können Sie für das maven-compiler-plugin auf der Apache Maven Project-Seite eine Liste aller Variablen sehen, die das Plugin steuern. Informationen zum Plugin sind unter dem Link verfügbar.
Goal ist eine spezielle Aufgabe, die sich auf das Erstellen und Verwalten eines Projekts bezieht. Die Hauptziele stimmen mit den Hauptphasen überein:

  • validate;
  • compile;
  • test;
  • package;
  • verify;
  • install;
  • deploy;

Wie bereits erwähnt, sind Plugins eine Reihe von Zielen. Beispielsweise enthält das Plug-in „maven-compiler-plugin“ zwei Ziele: „compiler:compile“ zum Kompilieren des Hauptquellcodes des Projekts und „compiler:testCompile“ zum Kompilieren von Tests.

2.3. Lifecycles

Es gibt drei Build-Lebenszyklen: default, clean und site. Der Standardlebenszyklus behandelt Ihre Projektbereitstellung, der Clean-Lebenszyklus die Projektbereinigung und der Site-Lebenszyklus erstellt die Website Ihres Projekts.

Build Lifecycles

Abbildung 1. Build Lebenszyklus [1]

Der Lebenszyklus besteht aus Phasen. Phasen können auch null oder mehr Ziele enthalten (Abb. 1). Wenn an eine Phase keine Ziele gebunden sind, wird diese Phase nicht ausgeführt. Aber wenn es ein oder mehrere Ziele daran gebunden hat, wird es alle diese Ziele ordnungsgemäß ausführen [2].

3. Zusammenhang der einzelnen Komponenten

Lifecycles beziehen sich auf den Lebenszyklus eines Projekts, von der Entwicklung bis zur Bereitstellung. Maven hat mehrere vordefinierte Lifecycles, wie zum Beispiel den „clean“ Lifecycle, der für das Reinigen des Projekts verwendet wird, und den „default“ Lifecycle, der für die Erstellung und Bereitstellung des Projekts verwendet wird.

Phases sind Teile eines Lifecycles, die bestimmte Aktionen ausführen. Zum Beispiel ist die „compile“ Phase Teil des „default“ Lifecycles und sorgt dafür, dass die Quelldateien des Projekts kompiliert werden. Jede Phase kann mehrere (oder keine) Goals enthalten, die spezifische Aufgaben ausführen.

Plugins sind Erweiterungen, die Maven ermöglichen, bestimmte Aktionen auszuführen. Jedes Plugin hat ein oder mehrere Goals, die von Maven aufgerufen werden können, um bestimmte Aktionen auszuführen. Zum Beispiel kann das „maven-compiler-plugin“ verwendet werden, um die Quelldateien des Projekts zu kompilieren, und das „maven-surefire-plugin“ kann verwendet werden, um die Unit-Tests des Projekts auszuführen.

In Maven werden Lifecycles, Phases und Plugins zusammengeführt, um ein Projekt von der Entwicklung bis zur Bereitstellung zu automatisieren. Wenn beispielsweise der Befehl „mvn package“ ausgeführt wird, wird Maven den „default“ Lifecycle ausführen, der mehrere Phases enthält, wie zum Beispiel die „compile“ Phase und die „test“ Phase. Jede Phase kann mehrere Goals enthalten, die von verschiedenen Plugins ausgeführt werden.

BPMN Default Lifecycle
BPMN Default Lifecycle

4. POM

In Apache Maven werden POM-Dateien (Project Object Model) genutzt, um Projekt- und Konfigurationsinformationen einheitlich in XML-Struktur zu speichern. POM-Dateien bieten strukturiertes Bibliotheksmanagement und haben hierarchische Struktur in Softwareprojekten, wodurch Projektinformationen, wie die Version einer eingebundenen Bibliothek nur innerhalb oberer POMs definiert wird und in tiefer gelegenen POMs nur referenziert werden muss. Innerhalb einer POM werden Informationen über die Datei- und Modulstruktur, eine Liste von eingebundenen projektinternen und externen Abhängigkeiten, Konfigurationseinstellungen mit denen verschiedene Maven Lifecycles und Plugins beschrieben werden oder auch die Repositories aus denen Abhängigkeiten bezogen werden, abgelegt.

Maven Coordinates

Jede POM-Datei enthält die Elemente groupId, artifactId und version, unter welchen die jeweilige POM in verschiedenen Repositories aufzufinden ist. Diese Attribute wirken dabei als Adresse und Zeitstempel einer Bibliotheksversion und werden auch als Maven Coordinates bezeichnet. Eine minimale POM beschreibt nur die eigene Gruppen- und Artefakt-ID mit einer gewählten Version, ohne Konfigurationseinstellungen oder andere Abhängigkeiten zu nutzen.

Abbildung 2 – Minimale POM [3]

Dependencies

Eine Hauptfunktion der POM ist die Verwaltung von projekt-internen und externen Abhängigkeiten. Hierfür werden in der POM unter dem Element <dependencies> verschiedene Abhängigkeitsnamen und -versionen definiert, mit denen Bibliotheksversionen aus öffentlichen Repositories geladen und lokal in einem .m2 Datenlager abgespeichert werden. Für die Nutzung von lizensierten Bibliotheken, sowie die Einbindung aller Abhängigkeiten, welche in keinen öffentlichen Datenlagern gefunden werden können, müssen Bibliotheksartefakte manuell in das .m2 Datenlager eingefügt werden. Dabei können nur Maven Artefakte einbezogen werden, ohne Möglichkeit der Nutzung von anderen Artefakttypen. Hierbei werden nicht nur explizit eingebundene Bibliotheken, sondern auch transitive Abhängigkeiten, welche von eingebundenen Bibliotheken genutzt werden, bezogen. Um mehrere Versionen derselben Bibliothek zu vermeiden, können Abhängigkeiten transitiv unter dem Element <exlusions> ausgeschlossen werden.

Alle Abhängigkeiten, welche in einer POM eingebunden werden, werden in der Abhängigkeitsliste der POM definiert. Zu jeder eingebundenen Bibliotheksversion müssen zumindest die Maven Coordinates groupId, artifactId und version angegeben werden. Folgende Informationen können zu jeder Abhängigkeit angegeben werden:

  • groupIdartifactIdversion: Maven Coordinates, über welche Bibliotheksversionen eingebunden werden
  • classifier: beschreibende Namenszusätze, durch die mehrere Artefakte aus einer einzelnen POM erstellt und eingebunden werden können, um beispielsweise unterschiedliche Artefakte für verschiedene Systeme, Umgebungen oder auch für API-Dokumentationen zu beziehen
  • type: einzubindender Abhängigkeitstyp, zur Nutzung von JAR-Artefakten (Java Archive), Test-JARs für Testdateien, weiteren POMs oder JavaDoc Dokumenten
  • scope: beschränkte Sichtbarkeit der eingebundenen Abhängigkeit
  • systemPath: absoluter Systempfad, über den das Artefakt bezogen wird, wenn scope auf system gesetzt ist
  • optional: Abhängigkeiten, welche nicht transitiv eingebunden werden, sollte die POM von anderen Modulen oder Projekten eingebunden werden

Inheritance

POM-Dateien erben Informationen und Abhängigkeiten aus hierarchisch übergeordneten POMs. Dies ist nützlich, damit beispielsweise Bibliotheksversionen, Lizenzinformationen, genutzte Plugins oder Build-Einstellungen nicht innerhalb jeder POM neu definiert werden müssen. Dabei ist es wichtig, dass übergeordnete POMs mit dem Attribut packaging, sowie dem Wert pom gekennzeichnet werden. Hier ist zu beachten, dass die Artefakt ID einer POM nicht vererbt wird und damit in jeder POM zugewiesen werden muss. Übergeordnete POMs werden mit dem Element <parent> gekennzeichnet, wobei alle POMs von einer vordefinierten Super POM mit vielen Standardkonfigurationen erben, ohne diese selbst angeben zu müssen.

Abbildung 3 – Vererbung mit Parent POM [3]

Aggregation

Bei der Aggregation können ähnlich zur Vererbung Maven Kommandos innerhalb einer POM definiert werden, welche dann für verschiedene Module, welche in der Aggregator-POM eingebunden werden, ebenfalls ausgeführt werden. Somit entsteht bei diesem Prinzip ein Multi-Modul, über welches Kommandos in Sub-Modulen selbstständig ausgeführt werden. Dabei ist die Reihenfolge von benannten Sub-Modulen, sowie deren transitive Einbindungen unwichtig.

Abbildung 4 – Aggregation mit Sub-Modulen [4]

5. Einrichtung im Projekt

Maven braucht keine umständlichen Installer und Einrichtungskonfigurationen, damit es genutzt werden kann. Es reicht, wenn Maven Befehle über die Kommandozeile ausführbar sind, indem der Pfad zum Maven Home Directory als Systemumgebungsvariable gesetzt ist.

Maven Wrapper

Es kann wünschenswert sein keine bestimmte Maven Version auf einem System zur Ausführung eines Projektes installieren zu müssen. Für diesen Fall kann Maven-Wrapper genutzt werden, welches keine systemweite Einrichtung von Maven voraussetzt. Es muss lediglich die Maven Wrapper Distribution gedownloadet und in der Umgebung des Projektes, welches Maven nutzen soll, mit dem Maven-Goal wrapper:wrapper ausgeführt werden.[5 ] Bei diesem Befehl kann ebenfalls eine ausgewählte Maven Version angegeben werden, welche anschließend wie eine eingebundene Bibliothek im .m2 Datenlager abgelegt wird. Nach Ausführung können typische Maven Befehle wie mvn clean install stattdessen durch ./mvnw clean install oder mvnw.cmd clean install (Windows) ausgeführt werden.

Getting Started

Falls Maven für ein Projekt genutzt werden soll, kann das Maven-Goal mvn archetype:generate genutzt, um die typische Maven Modulstruktur mir POM-Dateien zu initialisieren. [6] Anschließend können alle Maven Befehle, welche zur Entwicklung des Projektes beitragen genutzt werden, wie mvn compile, zur Kompilierung von Quellcode, mvn test zum Ausführen von Testklassen oder mvn clean zum Aufräumen von target Ordnern. Typische Maven Ausgaben sollten nun auch auf der Kommandozeile zu sehen sein.

6. Gradle vs. Maven

Gradle ist ein Build-Automation-Tool, das als Konkurrent zu Maven in der Java-Entwicklung gilt. Dabei unterstützt Gradle auch andere Sprachen wie C++, Python und Kotlin.

Gradle legt den Fokus besonders auf Performance, Benutzerfreundlichkeit, Wartbarkeit und Flexibilität und verwendet dabei „Tasks“ als Arbeitsunits, die den „Goals“ in Maven entsprechen. Dies ermöglicht es Gradle, noch mehr Flexibilität und Steuerung in Bezug auf die Art und Weise, wie Build-Prozesse ausgeführt werden, zu bieten.

Damit bietet Gradle eine leistungsstarke Alternative zu Maven und ist vielseitig genug, um in einer Vielzahl von Projekten eingesetzt zu werden. Es ist besonders für Benutzer geeignet, die mehr Flexibilität und Kontrolle in Bezug auf den Build-Prozess wünschen.

Im Folgenden werden einige Kategorien näher betrachtet, die zur Gegenüberstellung der beiden Tools dienen.

Beliebtheit & Bekanntheit

Maven ist das momentan beliebteste Build-Automation-Tool für Java auf dem Markt und ist auch im industriellen Bereich weiter verbreitet als Gradle. Die Beliebtheit kann ein wichtiger Faktor bei der Entscheidung zwischen verschiedenen Build-Automation-Tools sein, da mit einem weit verbreiteteren Tool oft einfacher zu arbeiten ist, da mehr Dokumentation, Erfahrungswerte und Expertenwissen zur Verfügung stehen. Gradle ist dabei im Vergleich zu Maven ein „Newcomer“. Die Anzahl der Entwickler, die gut mit Gradle vertraut sind, ist folglich begrenzt. Aufgrund der höheren Bekanntheit gibt es auch mehr Plugins für Maven als für Gradle.

Build-Skripts

Maven beschreibt Projektbuilds in der ausführlichen Markup-Sprache XML, während Gradle eine Domain-spezifische Sprache (DSL) verwendet, die auf den Programmiersprachen Groovy oder Kotlin basiert. Die Verwendung von tag-basiertem XML in Maven macht die Skripte im Vergleich zu code-basierten DSLs wie Gradle schwerer lesbar und verständlich, insbesondere bei größeren Projekten. Für viele Benutzer ist das build.gradle von Gradle einfacher zu lesen und zu pflegen als die pom.xml von Maven. Allerdings hat Gradle auch eine umfangreiche Dokumentation, was es erforderlich macht, sich vorheriges Wissen über bestimmte Begriffe anzueignen, um die Build-Skripte vollständig zu verstehen.

Performance

Gradle ist im Vergleich zu Maven in verschiedenen Build-Situationen deutlich schneller, in einigen Fällen sogar bis zu 85x schneller. Ein herausstechendes Performance-Features von Gradle sind inkrementelle Builds. Dadurch können unnötige Ausführungen von Tasks oder Testläufen verhindert werden. Gradle führt somit nur Tasks aus, wenn sie im vorherigen Build noch nicht ausgeführt wurden und sich deren Input oder Output verändert hat. Auf diese Weise werden Full-Rebuilds bei kleinen Codeänderungen vermieden. Ein weiteres Feature ist die inkrementelle Kompilierung, die das gleiche Konzept wie inkrementelle Builds bei Codeänderungen verfolgt. Schließlich gibt es den Gradle Daemon, der Gradle im Hintergrund kontinuierlich ausführt und bereithält, um einen Build auszuführen, sobald er angefordert wird.

Anpassungsmöglichkeiten

Gradle bietet viele Möglichkeiten, um die Buildlogik anzupassen und wiederzuverwenden. Dazu gehört das Hinzufügen eigener Logik innerhalb des Build-Skripts, ohne ein konkretes separates Plugin entwickeln zu müssen. So können Tasks, Methoden und Klassen definiert werden, die in anderen Teilen des Skripts wiederverwendet werden können. Zusätzlich können Elemente in einer eigenen Datei im „buildSrc“-Ordner abgelegt werden, die in Subprojekten wiederverwendet werden können. Die Wiederverwendung von Buildlogik hilft dabei beispielweise, Duplikationen zu vermeiden, insbesondere bei großen Projekten. Allerdings ist dabei notwendig, vorheriges Wissen über Gradle zu erwerben, um neue Tasks zu erstellen.

Im Gegensatz dazu hat Maven nur eine Option, um die Buildlogik anzupassen und wiederzuverwenden: das Schreiben eines eigenen Plugins. Dies erfordert jedoch zwingend die Erstellung eines separaten Projekts und die Veröffentlichung des Plugin-Artefakts zur Nutzung im Projekt.

Standardisierung

Maven bietet Entwicklern eine default Ordnerstruktur für Javaprojekte, die ihnen unter anderem dabei hilft, leichter zwischen Projekten zu wechseln. Diese Standardisierung der Ordnerstruktur erleichtert es Entwicklern, sich schneller in neuen Projekten zurechtzufinden und beschleunigt die Einarbeitung in das Projekt. Maven standardisiert auch, wie Javaprojekte grundsätzlich gebaut werden. Dies bedeutet, dass der Buildprozess eines Javaprojekts mithilfe von Maven in einer einheitlichen Art und Weise gesteuert wird, was wiederum die Wiederverwendbarkeit von Buildskripten erleichtert.

Gemeinsamkeiten

Maven und Gradle haben ebenfalls einige Gemeinsamkeiten in Bezug auf ihre Funktionalität. Beide bieten Plugins für verschiedene Zwecke an, wie beispielsweise die statische Code-Analyse oder das Berechnen der Testcode-Coverages. Diese Plugins erweitern die Funktionalität beider Tools und erleichtern es Entwicklern, bestimmte Aufgaben auszuführen. Über Artefakt Repositories können (transitive) Abhängigkeiten bezogen werden. Maven nutzt dabei das Maven Central Repository, während Gradle das JCenter Repository verwendet. Es ist auch möglich, ein eigenes privates Repository zu definieren und zu verwenden. Diese Gemeinsamkeiten erleichtern es Entwicklern, ihre Projekte aufzusetzen und zu verwalten.

Gradle vs. Maven – Fazit

Zusammengefasst lässt sich sagen, dass Gradle in Situationen verwendet werden sollte, in denen Vielseitigkeit, Geschwindigkeit und inkrementelle Builds von hoher Bedeutung sind. Dies macht es besonders gut geeignet für große Projekte, in denen schnelle Buildzeiten und die Vermeidung von unnötigen Ausführungen von Tasks wichtig sind. Maven hingegen sollte in Situationen verwendet werden, in denen Modularisierung, Dependency Management, Beständigkeit, Plugins und Konventionen (über Konfiguration) priorisiert werden. Maven eignet sich daher besser für kleine Projekte, in denen die Einhaltung von Konventionen und das Verwalten von Abhängigkeiten wichtig sind. Allerdings ist Maven auch für große Projekte geeignet, insbesondere wegen seiner umfangreichen Plugin-Sammlung und seiner konventionellen Ordnerstruktur.

 

Quellen

  1. https://www.oreilly.com/library/view/maven-essentials/9781783986767/ch05.html
  2. https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
  3. https://maven.apache.org/pom.html
  4. https://devflection.com/posts/2020-04-12-maven-part-3/
  5. https://maven.apache.org/wrapper/maven-wrapper-plugin/usage.html
  6. https://maven.apache.org/guides/getting-started/index.html
  7. https://medium.com/@yetanothersoftwareengineer/maven-lifecycle-phases-plugins-and-goals-25d8e33fa22
  8. https://dzone.com/articles/gradle-vs-maven
  9. https://tomgregory.com/maven-vs-gradle-comparison/
  10. https://www.interviewbit.com/blog/gradle-vs-maven/
  11. https://www.geeksforgeeks.org/difference-between-gradle-and-maven/
  12. https://medium.com/@yetanothersoftwareengineer/maven-lifecycle-phases-plugins-and-goals-25d8e33fa22
  13. https://cguntur.me/2020/05/29/understanding-apache-maven-part-4/
  14. https://www.logicbig.com/tutorials/build-tools/apache-maven/maven-lifecycle-phases-goals.html
  15. https://www.appsdeveloperblog.com/maven-goals-and-phases-tutorial/
  16. https://www.baeldung.com/maven-goals-phases