Eine Einführung in Maven 2

Maven ist ein beliebtes Open Source-Build-Tool für Java-Unternehmensprojekte, mit dem ein Großteil der harten Arbeit aus dem Build-Prozess herausgenommen werden kann. Maven verwendet einen deklarativen Ansatz, bei dem die Projektstruktur und der Inhalt beschrieben werden, und nicht den aufgabenbasierten Ansatz, der beispielsweise in Ant oder in herkömmlichen Make-Dateien verwendet wird. Dies hilft bei der Durchsetzung unternehmensweiter Entwicklungsstandards und reduziert den Zeitaufwand für das Schreiben und Verwalten von Build-Skripten.

Der deklarative, auf dem Lebenszyklus basierende Ansatz von Maven 1 ist für viele eine radikale Abkehr von traditionelleren Bautechniken, und Maven 2 geht in dieser Hinsicht noch weiter. In diesem Artikel gehe ich einige der Grundprinzipien hinter Maven 2 durch und gehe dann ein funktionierendes Beispiel durch. Beginnen wir mit der Überprüfung der Grundlagen von Maven 2.

Das Projektobjektmodell

Das Herzstück eines Maven 2-Projekts ist das Projektobjektmodell (kurz POM). Es enthält eine detaillierte Beschreibung Ihres Projekts, einschließlich Informationen zu Versions- und Konfigurationsmanagement, Abhängigkeiten, Anwendungs- und Testressourcen, Teammitgliedern und -struktur sowie vielem mehr. Das POM hat die Form einer XML-Datei ( pom.xml ), die in Ihrem Projekt- Ausgangsverzeichnis abgelegt wird. Eine einfache pom.xml-Datei wird hier gezeigt:

 4.0.0 com.javaworld.hotels HotelDatabase war 1.0-SNAPSHOT Maven Quick Start Archetype //maven.apache.org   junit junit 3.8.1 test   

Die Maven 2-Verzeichnisstruktur

Ein Großteil von Mavens Macht kommt von den Standardpraktiken, die es fördert. Ein Entwickler, der zuvor an einem Maven-Projekt gearbeitet hat, wird sich sofort mit der Struktur und Organisation eines neuen vertraut fühlen. Es muss keine Zeit verschwendet werden, Verzeichnisstrukturen, Konventionen und angepasste Ant-Build-Skripte für jedes Projekt neu zu erfinden. Obwohl Sie einen bestimmten Verzeichnisspeicherort für Ihre eigenen Zwecke überschreiben können, sollten Sie die Standard-Maven 2-Verzeichnisstruktur aus mehreren Gründen so weit wie möglich respektieren:

  • Dadurch wird Ihre POM-Datei kleiner und einfacher
  • Es erleichtert das Verständnis des Projekts und erleichtert dem armen Mann das Leben, der das Projekt beim Verlassen des Projekts aufrechterhalten muss
  • Es erleichtert die Integration von Plug-Ins

Die Standard-Maven 2-Verzeichnisstruktur ist in Abbildung 1 dargestellt. Im Home-Verzeichnis des Projekts befinden sich das POM (pom.xml) und zwei Unterverzeichnisse: src für den gesamten Quellcode und das Ziel für generierte Artefakte.

Das src-Verzeichnis verfügt über eine Reihe von Unterverzeichnissen, von denen jedes einen klar definierten Zweck hat:

  • src / main / java: Dein Java-Quellcode geht hierher (seltsamerweise!)
  • src / main / resources: Andere Ressourcen, die Ihre Anwendung benötigt
  • src / main / filter: Ressourcenfilter in Form von Eigenschaftendateien, mit denen Variablen definiert werden können, die nur zur Laufzeit bekannt sind
  • src / main / config: Konfigurationsdateien
  • src / main / webapp: Das Webanwendungsverzeichnis für ein WAR-Projekt
  • src / test / java: Unit-Tests
  • src / test / resources: Ressourcen, die für Komponententests verwendet werden sollen, jedoch nicht bereitgestellt werden
  • src / test / filter: Ressourcenfilter, die für Komponententests verwendet werden sollen, jedoch nicht bereitgestellt werden
  • src / site: Dateien, die zum Generieren der Maven-Projektwebsite verwendet werden

Projektlebenszyklen

Projektlebenszyklen spielen bei Maven 2 eine zentrale Rolle. Die meisten Entwickler sind mit dem Begriff der Erstellungsphasen wie Kompilieren, Testen und Bereitstellen vertraut. Ant hat Ziele mit solchen Namen. In Maven 1 werden entsprechende Plug-Ins direkt aufgerufen. Zum Kompilieren von Java-Quellcode wird beispielsweise das javaPlug-In verwendet:

$maven java:compile

In Maven 2 ist dieser Begriff in eine Reihe bekannter und genau definierter Lebenszyklusphasen standardisiert (siehe Abbildung 2). Anstatt Plug-Ins aufzurufen, ruft der Maven 2-Entwickler eine Lebenszyklusphase auf : $mvn compile.

Einige der nützlicheren Maven 2-Lebenszyklusphasen sind die folgenden:

  • generate-sources: Erzeugt zusätzlichen Quellcode, der für die Anwendung benötigt wird. Dies wird im Allgemeinen mit den entsprechenden Plug-Ins erreicht
  • compile: Kompiliert den Projektquellcode
  • test-compile: Kompiliert die Projekt-Unit-Tests
  • test: Führt die Komponententests (normalerweise mit JUnit) im Verzeichnis src / test aus
  • package: Packt den kompilierten Code in sein verteilbares Format (JAR, WAR usw.)
  • integration-test: Verarbeitet und stellt das Paket bei Bedarf in einer Umgebung bereit, in der Integrationstests ausgeführt werden können
  • install: Installiert das Paket im lokalen Repository, um es als Abhängigkeit in anderen Projekten auf Ihrem lokalen Computer zu verwenden
  • deploy: In einer Integrations- oder Release-Umgebung wird das endgültige Paket in das Remote-Repository kopiert, um es für andere Entwickler und Projekte freizugeben

Viele andere Lebenszyklusphasen sind verfügbar. Weitere Informationen finden Sie unter Ressourcen.

Diese Phasen veranschaulichen die Vorteile der von Maven 2 empfohlenen empfohlenen Vorgehensweisen: Sobald ein Entwickler mit den Hauptphasen des Maven 2-Lebenszyklus vertraut ist, sollte er sich mit den Lebenszyklusphasen eines Maven-Projekts wohl fühlen.

In der Lebenszyklusphase werden die Plug-Ins aufgerufen, die für die Ausführung der Aufgabe erforderlich sind. Durch das Aufrufen einer Lebenszyklusphase werden automatisch auch alle vorherigen Lebenszyklusphasen aufgerufen. Da die Anzahl der Lebenszyklusphasen begrenzt, leicht verständlich und gut organisiert ist, ist es einfach, sich mit dem Lebenszyklus eines neuen Maven 2-Projekts vertraut zu machen.

Transitive Abhängigkeiten

Eines der Highlights von Maven 2 ist das transitive Abhängigkeitsmanagement. Wenn Sie jemals ein Tool wie urpmi auf einer Linux-Box verwendet haben, wissen Sie, was transitive Abhängigkeiten sind. Mit Maven 1 müssen Sie jede einzelne JAR deklarieren, die direkt oder indirekt von Ihrer Anwendung benötigt wird. Können Sie beispielsweise die JARs auflisten, die von einer Hibernate-Anwendung benötigt werden? Mit Maven 2 müssen Sie nicht. Sie teilen Maven einfach mit, welche Bibliotheken Sie benötigen, und Maven kümmert sich um die Bibliotheken, die Ihre Bibliotheken benötigen (und so weiter).

Angenommen, Sie möchten den Ruhezustand in Ihrem Projekt verwenden. Sie würden dem dependenciesAbschnitt in pom.xml einfach wie folgt eine neue Abhängigkeit hinzufügen :

  hibernate hibernate 3.0.3 compile 

Und das ist es! Sie müssen nicht herumjagen, um zu wissen, in welchen anderen JARs (und in welchen Versionen) Sie Hibernate 3.0.3 ausführen müssen. Maven wird es für Sie tun!

Die XML-Struktur für Abhängigkeiten in Maven 2 ähnelt der in Maven 1 verwendeten. Der Hauptunterschied ist das scopeTag, das im folgenden Abschnitt erläutert wird.

Abhängigkeitsbereiche

In einer realen Unternehmensanwendung müssen Sie möglicherweise nicht alle Abhängigkeiten in die bereitgestellte Anwendung aufnehmen. Einige JARs werden nur für Komponententests benötigt, während andere zur Laufzeit vom Anwendungsserver bereitgestellt werden. Mit einer Technik namens Abhängigkeitsbereich können Sie in Maven 2 bestimmte JARs nur dann verwenden, wenn Sie sie wirklich benötigen, und sie aus dem Klassenpfad ausschließen, wenn Sie dies nicht tun.

Maven bietet vier Abhängigkeitsbereiche:

  • compile: In allen Phasen ist eine Abhängigkeit vom Kompilierungsbereich verfügbar. Dies ist der Standardwert.
  • provided: Eine bereitgestellte Abhängigkeit wird zum Kompilieren der Anwendung verwendet, jedoch nicht bereitgestellt. Sie würden diesen Bereich verwenden, wenn Sie erwarten, dass das JDK oder der Anwendungsserver die JAR bereitstellt. Die Servlet-APIs sind ein gutes Beispiel.
  • runtime: Abhängigkeiten vom Laufzeitbereich werden für die Kompilierung nicht benötigt, sondern nur für die Ausführung, z. B. JDBC-Treiber (Java Database Connectivity).
  • test: Testbereichsabhängigkeiten werden nur zum Kompilieren und Ausführen von Tests benötigt (z. B. JUnit).

Projektkommunikation

Ein wichtiger Teil jedes Projekts ist die interne Kommunikation. Obwohl es sich nicht um eine Silberkugel handelt, kann eine zentralisierte Website für technische Projekte einen großen Beitrag zur Verbesserung der Sichtbarkeit im Team leisten. Mit minimalem Aufwand können Sie in kürzester Zeit eine professionelle Projektwebsite einrichten.

Dies nimmt eine völlig neue Dimension an, wenn die Maven-Site-Generierung mithilfe kontinuierlicher Integration oder sogar automatischer nächtlicher Builds in einen Erstellungsprozess integriert wird. Eine typische Maven-Site kann täglich Folgendes veröffentlichen:

  • Allgemeine Projektinformationen wie Quell-Repositorys, Fehlerverfolgung, Teammitglieder usw.
  • Unit Test und Test Coverage Reports
  • Automatische Codeüberprüfung und mit Checkstyle und PMD
  • Konfigurations- und Versionsinformationen
  • Abhängigkeiten
  • Javadoc
  • Quellcode im indizierten und referenzierten HTML-Format
  • Liste der Teammitglieder
  • Und vieles mehr

Wieder einmal weiß jeder Maven-versierte Entwickler sofort, wo er suchen muss, um sich mit einem neuen Maven 2-Projekt vertraut zu machen.

Ein praktisches Beispiel

Nachdem wir einige der in Maven 2 verwendeten Grundbegriffe gesehen haben, wollen wir sehen, wie es in der realen Welt funktioniert. Der Rest dieses Tutorials untersucht, wie wir Maven 2 in einem einfachen Java Enterprise Edition-Projekt verwenden würden. Die Demo-Anwendung beinhaltet ein imaginäres (und vereinfachtes) Hoteldatenbanksystem. Um zu demonstrieren, wie Maven mit Abhängigkeiten zwischen Projekten und Komponenten umgeht, wird diese Anwendung mit zwei Komponenten erstellt (siehe Abbildung 3):

  • Eine Geschäftslogikkomponente: HotelDatabase.jar
  • Eine Webanwendungskomponente: HotelWebApp.war

Sie können den Quellcode herunterladen, der zusammen mit dem Tutorial unter Ressourcen folgt.

Richten Sie Ihre Projektumgebung ein

We start by configuring your work environment. In real-world projects, you will often need to define and configure environment or user-specific parameters that should not be distributed to all users. If you are behind a firewall with a proxy, for example, you need to configure the proxy settings so that Maven can download JARs from repositories on the Web. For Maven 1 users, the build.properties and project.properties files do this job. In Maven 2, they have been replaced by a settings.xml file, which goes in the $HOME/.m2 directory. Here is an example:

     http scott tiger 8080 my.proxy.url    

Create a new project with the archetype plug-in

The next step is to create a new Maven 2 project template for the business logic component. Maven 2 provides the archetype plug-in, which builds an empty Maven 2-compatible project directory structure. This plug-in proves convenient for getting a basic project environment up and running quickly. The default archetype model will produce a JAR library project. Several other artifact types are available for other specific project types, including Web applications, Maven plug-ins, and others.

Run the following command to set up your HotelDatabase.jar project:

mvn archetype:create -DgroupId=com.javaworld.hotels - DartifactId=HotelDatabase -Dpackagename=com.javaworld.hotels

Now you have a brand new Maven 2 project directory structure. Switch to the HotelDatabase directory to continue the tutorial.

Implementing the business logic

Jetzt implementieren wir die Geschäftslogik. Die HotelKlasse ist eine einfache JavaBean. Die HotelModelKlasse implementiert zwei Dienste: die findAvailableCities()Methode, mit der verfügbare Städte aufgelistet werden, und die findHotelsByCity()Methode, mit der alle Hotels in einer bestimmten Stadt aufgelistet werden. Eine einfache, speicherbasierte Implementierung der HotelModelKlasse wird hier vorgestellt:

package com.javaworld.hotels.model;

import java.util.ArrayList; import java.util.List;

import com.javaworld.hotels.businessobjects.Hotel;

public class HotelModel {

/** * The list of all known cities in the database. */ private static String[] cities = { "Paris", "London", }; /** * The list of all hotels in the database. */ private static Hotel[] hotels = { new Hotel("Hotel Latin","Quartier latin","Paris",3), new Hotel("Hotel Etoile","Place de l'Etoile","Paris",4), new Hotel("Hotel Vendome","Place Vendome","Paris",5), new Hotel("Hotel Hilton","Trafalgar Square","London",4), new Hotel("Hotel Ibis","The City","London",3), }; /** * Returns the hotels in a given city. * @param city the name of the city * @return a list of Hotel objects */ public List findHotelsByCity(String city){ List hotelsFound = new ArrayList(); for(Hotel hotel : hotels) { if (hotel.getCity().equalsIgnoreCase(city)) { hotelsFound.add(hotel); } } return hotelsFound; } /** * Returns the list of cities in the database which have a hotel. * @return a list of city names */ public String[] findAvailableCities() { return cities; } }