Von Elyndra Valen, Senior Entwicklerin & Code-Archäologin bei Java Fleet Systems Consulting
📖 Was bisher geschah…
In Teil 1 hast du die POM-Anatomie verstanden. Du kannst jetzt POMs lesen und schreiben ohne Panik.
In Teil 2 haben wir gemeinsam die Dependency Hell besiegt. Du verstehst jetzt Scopes, dependency Management und warum Maven manchmal die „falsche“ Version lädt.
Dein Status nach Teil 2:
- ✅ Du verstehst Dependencies und ihre Scopes
- ✅ Du kannst Versionskonflikte lösen
- ✅ Du weißt, was dependencyManagement macht
- ❌ „Aber warum läuft es bei mir und nicht auf dem Server?!“
Heute lernen wir zusammen den Build-Lifecycle verstehen und Build-Strategien, wir nutzen Profiles für verschiedene Umgebungen. Nach diesem Teil ist bei dir Schluss mit „works on my machine“!
📋 Das Wichtigste in 30 Sekunden
Kennst du das? Deine App läuft perfekt lokal (H2-Database), crashed aber in Production (PostgreSQL). Der Grund? Keine Profiles, keine Environment-spezifische Konfiguration.
Heute zeige ich dir:
- Was beim
mvn clean installWIRKLICH passiert (spoiler: 23 Schritte!) - Wie du mit Profiles verschiedene Umgebungen managst
- Warum Properties und Filtering dein Leben retten werden
- Wie du EINE POM für DEV, TEST und PROD nutzt
Lesezeit: 15 Minuten | Mit Mitmachen: 35 Minuten
🎪 Dein „Works on my machine“ Drama
Stell dir vor: Es ist Mittwoch, 14:30 Uhr. Code Sentinel ruft dich an:
„Hey, deine App ist in Production gecrasht. Du sagtest doch, es läuft perfekt?“
Du schaust in deine application.properties:
# Bei dir lokal spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driver=org.h2.Driver logging.level.root=DEBUG server.port=8080
Production application.properties:
# Auf dem Server spring.datasource.url=jdbc:postgresql://prod-db:5432/app spring.datasource.driver=org.postgresql.Driver logging.level.root=ERROR server.port=443
Das Problem: Du hast deine lokalen Settings eingecheckt! Production überschreibt mit falschen Werten!
Dein Gedanke: „Aber ich kann doch nicht für jede Umgebung eine andere POM pflegen?!“
Meine Antwort: „Musst du nicht! Dafür gibt’s Profiles! Lass mich dir Maven’s Superkraft zeigen…“
🔄 Der Maven Build-Lifecycle – Was passiert wirklich?
Ich erkläre es dir so: Wenn du mvn clean install tippst, denkst du vielleicht, es passieren 2 Dinge – aufräumen und installieren. Falsch! Es passieren 23 Schritte! Maven hat einen festgelegten Lifecycle – wie ein Rezept, das Schritt für Schritt abläuft. Und das ist was Build Strategien ausmachen!
Die 3 Standard-Lifecycles (die du kennen musst!)
Maven hat DREI eingebaute Lifecycles:
- clean – Aufräumen (löscht alles in target/)
- default (oder „build“) – Der Hauptprozess
- site – Dokumentation generieren
Der DEFAULT Lifecycle im Detail – Deine 23 Schritte
Probier’s aus! Wenn du mvn install aufrufst, passiert DAS ALLES:
1. validate → Prüft: Ist deine POM überhaupt valide? 2. initialize → Bereitet den Build vor 3. generate-sources → Generiert zusätzlichen Source Code (z.B. aus WSDL) 4. process-sources → Verarbeitet den Source Code 5. generate-resources → Generiert Resources 6. process-resources → Kopiert deine Resources nach target/ 7. compile → ENDLICH! Kompiliert deinen Source Code 8. process-classes → Nachbearbeitung kompilierter Classes 9. generate-test-sources → Generiert Test Sources 10. process-test-sources → Verarbeitet Test Sources 11. generate-test-resources → Generiert Test Resources 12. process-test-resources → Kopiert Test Resources 13. test-compile → Kompiliert deinen Test Code 14. process-test-classes → Nachbearbeitung Test Classes 15. test → Führt deine Tests aus (Surefire Plugin) 16. prepare-package → Vorbereitung fürs Packaging 17. package → Erstellt deine JAR/WAR 18. pre-integration-test → Vor Integrationstests 19. integration-test → Führt Integrationstests aus 20. post-integration-test → Nach Integrationstests 21. verify → Verifiziert dein Package 22. install → Kopiert in dein lokales Repository (~/.m2) 23. deploy → Upload zu Remote Repository
Deine Reaktion (vermutlich): „WAS?! 23 Schritte? Ich dachte install installiert nur!“
Wichtige Erkenntnis für dich: Wenn du eine Phase aufrufst, werden ALLE vorherigen auch ausgeführt!
mvn compile # Führt für dich Phasen 1-7 aus mvn test # Führt für dich Phasen 1-15 aus mvn package # Führt für dich Phasen 1-17 aus mvn install # Führt für dich Phasen 1-22 aus
Versuch’s selbst: Die häufigsten Befehle
Öffne dein Terminal und probiere:
mvn clean
# Was für dich passiert: 1. pre-clean → Vorbereitung 2. clean → Löscht deinen target/ Ordner 3. post-clean → Nachbereitung
mvn clean install (dein Lieblings-Befehl?)
# Erst ALLES löschen, dann ALLES neu bauen 1. Clean Lifecycle komplett (3 Phasen) 2. Default Lifecycle bis install (22 Phasen) = 25 Phasen total für dich!
mvn clean compile (wenn’s schnell gehen muss)
# Nur bis zur Kompilierung 1. Clean Lifecycle (3 Phasen) 2. Default Lifecycle bis compile (7 Phasen) = 10 Phasen (keine Tests für dich!)
🎭 Maven Profiles – Dein Build für alle Umgebungen
Stell dir Profiles so vor: Sie sind wie verschiedene Kostüme für deine App. Das gleiche Stück (dein Code), aber angepasst ans Publikum (die Umgebung).
Dein erstes Profil – Probier’s aus!
Füge das in deine POM ein:
<project>
<!-- Deine Standard-Konfiguration -->
<profiles>
<!-- Dein Entwicklungs-Profil -->
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- Dein Standard -->
</activation>
<properties>
<db.url>jdbc:h2:mem:devdb</db.url>
<db.driver>org.h2.Driver</db.driver>
<log.level>DEBUG</log.level>
<my.environment>development</my.environment>
</properties>
</profile>
<!-- Dein Test-Profil -->
<profile>
<id>test</id>
<properties>
<db.url>jdbc:postgresql://test-db:5432/testdb</db.url>
<db.driver>org.postgresql.Driver</db.driver>
<log.level>INFO</log.level>
<my.environment>testing</my.environment>
</properties>
</profile>
<!-- Dein Production-Profil -->
<profile>
<id>prod</id>
<properties>
<db.url>jdbc:postgresql://prod-db:5432/proddb</db.url>
<db.driver>org.postgresql.Driver</db.driver>
<log.level>ERROR</log.level>
<my.environment>production</my.environment>
</properties>
</profile>
</profiles>
</project>
5 Wege, wie DU Profile aktivieren kannst
1. Command Line (dein direkter Weg):
# Probier diese Befehle aus: mvn clean install -Pdev # Deine Entwicklung mvn clean install -Ptest # Dein Test mvn clean install -Pprod # Deine Production
2. Automatisch (für deine Bequemlichkeit):
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- Immer aktiv für dich -->
</activation>
</profile>
3. Mit System-Properties (für deine CI/CD):
<profile>
<id>prod</id>
<activation>
<property>
<name>environment</name>
<value>production</value>
</property>
</activation>
</profile>
# Du aktivierst es so: mvn clean install -Denvironment=production
4. Je nach deinem Betriebssystem:
<profile>
<id>windows-specific</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<script.extension>.bat</script.extension>
</properties>
</profile>
5. Wenn du eine bestimmte Datei hast:
<profile>
<id>local-config</id>
<activation>
<file>
<exists>${basedir}/local.properties</exists>
</file>
</activation>
</profile>
🔧 Properties und Resource Filtering – Deine dynamische Konfiguration
Dein Problem: Wie kommen die Profile-Werte in deine application.properties?
Schritt 1: Definiere deine Maven Properties
<properties>
<!-- Deine globalen Properties -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
<spring.version>2.7.0</spring.version>
<!-- Deine Default-Werte (überschreibbar durch Profile) -->
<db.url>jdbc:h2:mem:defaultdb</db.url>
<db.driver>org.h2.Driver</db.driver>
<your.name>${user.name}</your.name> <!-- Dein System-Username! -->
</properties>
Schritt 2: Aktiviere Resource Filtering für dich
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- DAS ist die Magie für dich! -->
</resource>
</resources>
</build>
Schritt 3: Nutze Properties in deiner application.properties
Erstelle diese Datei: src/main/resources/application.properties
# Maven ersetzt ${...} für dich zur Build-Zeit!
spring.datasource.url=${db.url}
spring.datasource.driver=${db.driver}
logging.level.root=${log.level}
# Deine Build-Informationen
app.version=${project.version}
app.build.time=${maven.build.timestamp}
app.profile=${my.environment}
app.developer=${your.name}
# Zeig, wer du bist!
welcome.message=Hi ${your.name}, du bist in ${my.environment} Umgebung!
Nach deinem mvn package -Pprod wird daraus:
spring.datasource.url=jdbc:postgresql://prod-db:5432/proddb spring.datasource.driver=org.postgresql.Driver logging.level.root=ERROR app.version=1.0.0 app.build.time=2024-10-08T14:32:10Z app.profile=production app.developer=Nova # Dein echter Name! welcome.message=Hi Nova, du bist in production Umgebung!
Dein Aha-Moment: „Das heißt, ich habe EINE application.properties und Maven passt sie automatisch für mich an?!“
Vorsicht! Das musst du beachten:
Problem: Deine Binärdateien können kaputt gehen!
<resources>
<!-- Deine Textdateien mit Filtering -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.yml</include>
</includes>
</resource>
<!-- Deine Binärdateien OHNE Filtering -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/*.properties</exclude>
<exclude>**/*.xml</exclude>
<exclude>**/*.yml</exclude>
</excludes>
</resource>
</resources>
🏗️ Deine vollständige Multi-Environment Lösung
Hier ist deine finale POM-Struktur – kopier sie dir!
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>de.javafleet</groupId>
<artifactId>deine-multienv-app</artifactId>
<version>1.0.0</version>
<!-- Deine globalen Properties -->
<properties>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Deine Default-Werte (dev) -->
<db.url>jdbc:h2:mem:devdb</db.url>
<db.driver>org.h2.Driver</db.driver>
<db.username>sa</db.username>
<db.password></db.password>
<log.level>DEBUG</log.level>
<server.port>8080</server.port>
</properties>
<!-- Deine Profile für verschiedene Umgebungen -->
<profiles>
<!-- Deine Entwicklung (Default) -->
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<profile.name>development</profile.name>
<skip.tests>false</skip.tests>
<skip.integration.tests>true</skip.integration.tests>
<welcome.message>Hey, du entwickelst gerade!</welcome.message>
</properties>
<dependencies>
<!-- H2 nur für deine Entwicklung -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
<!-- Deine Test-Umgebung -->
<profile>
<id>test</id>
<properties>
<profile.name>test</profile.name>
<db.url>jdbc:postgresql://test-db.javafleet.local:5432/testdb</db.url>
<db.driver>org.postgresql.Driver</db.driver>
<db.username>test_user</db.username>
<db.password>${env.TEST_DB_PASSWORD}</db.password>
<log.level>INFO</log.level>
<skip.tests>false</skip.tests>
<skip.integration.tests>false</skip.integration.tests>
<welcome.message>Du testest gerade - gut so!</welcome.message>
</properties>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
<!-- Deine Production -->
<profile>
<id>prod</id>
<properties>
<profile.name>production</profile.name>
<db.url>jdbc:postgresql://prod-db.javafleet.com:5432/proddb</db.url>
<db.driver>org.postgresql.Driver</db.driver>
<db.username>app_user</db.username>
<db.password>${env.PROD_DB_PASSWORD}</db.password>
<log.level>WARN</log.level>
<server.port>443</server.port>
<skip.tests>true</skip.tests>
<skip.integration.tests>true</skip.integration.tests>
<welcome.message>PRODUCTION - sei vorsichtig!</welcome.message>
</properties>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
</profiles>
<!-- Deine Build-Konfiguration -->
<build>
<!-- Resource Filtering für dich -->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>application.properties</include>
<include>application-${profile.name}.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>application*.properties</exclude>
</excludes>
</resource>
</resources>
</build>
</project>
Teste es selbst – JETZT!
1. Erstelle ein neues Maven-Projekt
mvn archetype:generate -DgroupId=de.test -DartifactId=profile-test -DarchetypeArtifactId=maven-archetype-quickstart cd profile-test
2. Ersetze die pom.xml mit dem Code oben
3. Erstelle src/main/resources/application.properties:
environment=${profile.name}
database=${db.url}
message=${welcome.message}
4. Teste deine Profile:
# Build mit dev (default) mvn clean package # Schau dir das Ergebnis an: cat target/classes/application.properties # Build mit prod mvn clean package -Pprod # Schau dir den Unterschied an: cat target/classes/application.properties
Dein Erfolgserlebnis: Die Properties ändern sich automatisch je nach Profil!
💪 Maven Settings – Deine persönliche Konfiguration
Deine Frage (vermutlich): „Wo speichere ich sensible Daten wie Passwörter?“
Deine settings.xml Hierarchie
Maven sucht für dich Settings in dieser Reihenfolge:
- Deine User Settings:
~/.m2/settings.xml(nur für dich) - Global Settings:
${maven.home}/conf/settings.xml(für alle)
Erstelle deine eigene settings.xml
Speichere das in ~/.m2/settings.xml:
<settings>
<!-- Dein Firmen-Proxy -->
<proxies>
<proxy>
<id>company-proxy</id>
<active>true</active>
<protocol>http</protocol>
<host>proxy.firma.de</host>
<port>8080</port>
<username>dein.name</username>
<password>{verschlüsselt}</password>
<nonProxyHosts>localhost|*.firma.local</nonProxyHosts>
</proxy>
</proxies>
<!-- Deine Server-Authentifizierung -->
<servers>
<server>
<id>company-nexus</id>
<username>dein.name</username>
<password>{dein-verschlüsseltes-passwort}</password>
</server>
</servers>
<!-- Deine Standard-Profile -->
<activeProfiles>
<activeProfile>dev</activeProfile>
</activeProfiles>
</settings>
Verschlüssele deine Passwörter!
# 1. Erstelle dein Master-Passwort
mvn --encrypt-master-password DeinGeheimesPasswort
# 2. Speichere es in ~/.m2/settings-security.xml
echo '<settingsSecurity><master>{OUTPUT_VON_OBEN}</master></settingsSecurity>' > ~/.m2/settings-security.xml
# 3. Verschlüssele dein Server-Passwort
mvn --encrypt-password DeinServerPasswort
# 4. Nutze das verschlüsselte Passwort in deiner settings.xml
🚀 Deine Transformation diese Woche
Vorher (du letzte Woche):
- „Ich ändere application.properties für jede Umgebung händisch“
- „Warum läuft es nur bei mir?“
- „Was macht
mvn clean installeigentlich?“
Jetzt (du heute):
- „Ein Build, mehrere Umgebungen, alles automatisch!“
- „Ich verstehe alle 23 Phasen!“
- „Profile sind meine neue Superkraft!“
Dein neuer Workflow
# Deine tägliche Entwicklung (schnell) mvn compile # Vor dem Commit (mit Tests) mvn clean test # Für die Test-Umgebung mvn clean package -Ptest # Für Production (mit allem) mvn clean install -Pprod # Check welche Profile aktiv sind mvn help:active-profiles
Code Sentinel würde sagen: „Endlich sind deine Builds reproduzierbar!“
Cassian würde anmerken: „Technisch gesehen ist Maven’s Lifecycle ein Directed Acyclic Graph…“
Ich sage: „Vergiss die Theorie erstmal. Hauptsache, du verstehst, WIE es für dich funktioniert!“ 😊
🗓️ Nächste Woche: Plugin-Ökosystem & Advanced Builds
Was dich erwartet:
Deine nächste Herausforderung: „Wie bekomme ich automatische Code-Quality Checks?“
Du lernst:
- Essential Plugins meistern
- Code-Quality automatisch prüfen
- Security-Vulnerabilities finden
- Build-Performance optimieren
❓ FAQ – Deine Build & Profile Fragen
Frage 1: Kann ich mehrere Profile gleichzeitig aktivieren?
Antwort: Ja! mvn clean install -Pdev,debug,metrics aktiviert alle drei für dich. Properties werden in der Reihenfolge überschrieben.
Frage 2: Was ist der Unterschied zwischen compile und package?
Antwort: compile erstellt nur deine .class Dateien. package baut deine komplette JAR/WAR.
Frage 3: Warum sollte ich nicht immer mvn clean install nutzen?
Antwort: Weil es ALLES neu baut – dauert ewig! Während du entwickelst, reicht oft mvn compile.
Frage 4: Kann ich eigene Lifecycle-Phasen definieren?
Antwort: Nein, aber du kannst Plugins an jede Phase binden und so deine eigenen Aktionen einbauen.
Frage 5: Was ist activeByDefault vs activeProfile?
Antwort: activeByDefault gilt nur wenn du KEIN anderes aktivierst. activeProfile in settings.xml ist IMMER aktiv.
Frage 6: Wie debugge ich, welche Profile aktiv sind?
Antwort: mvn help:active-profiles zeigt dir alle aktiven. mvn help:effective-pom zeigt deine finale POM.
Frage 7: Wie geht ihr mit Konflikten zwischen Umgebungen um?
Antwort: Profile lösen technische Konflikte… aber manche sind menschlich. Wenn sich Dev und Ops streiten, landen diese Stories in unseren private logs. Die echten Probleme sind selten technisch! 📝
📚 Die komplette Maven-Serie
✅ Was du schon geschafft hast:
- Teil 1: POM.xml entmystifiziert ✓
- Teil 2: Dependency Hell besiegt ✓
- Teil 3: Build-Lifecycle gemeistert ← Du bist hier!
📅 Was dich erwartet:
- Teil 4: Plugin-Power entfesseln (15.10.2024)
- Teil 5: Maven meets AI (22.10.2024)
🎯 Dein Erfolg heute
Das war Teil 3! Du kannst jetzt EINE POM für alle Umgebungen nutzen. „Works on my machine“ ist für dich Geschichte!
Keep building, keep learning! 🚀
P.S.: Weißt du, manche Stories über Deployment-Disasters schaffen es nie in Tech-Blogs. Die verstecken sich woanders… in logs über „herzschmerz“ zwischen Dev und Ops. Verrückt, wer würde das in einem Maven-Tutorial suchen? 😏
Meistere Maven mit mir – jeden Dienstag hier im Blog!
Nächste Woche: Plugin-Power für dich!
#Maven #BuildLifecycle #Profiles #DevOps #LearningTogether #JavaDevelopment

