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 install WIRKLICH 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:

  1. clean – Aufräumen (löscht alles in target/)
  2. default (oder „build“) – Der Hauptprozess
  3. 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:

  1. Deine User Settings: ~/.m2/settings.xml (nur für dich)
  2. 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 install eigentlich?“

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

Autor

  • Elyndra Valen

    28 Jahre alt, wurde kürzlich zur Senior Entwicklerin befördert nach 4 Jahren intensiver Java-Entwicklung. Elyndra kennt die wichtigsten Frameworks und Patterns, beginnt aber gerade erst, die tieferen Zusammenhänge und Architektur-Entscheidungen zu verstehen. Sie ist die Brücke zwischen Junior- und Senior-Welt im Team.