1. Wann eine einzelne application.yaml nicht ausreicht
Wenn die Grundeinstellungen bereits in einer application.yaml gesammelt sind, taucht ziemlich schnell der folgende Schmerz auf: Ein Teil der Werte ist für alle Ausführungen gemeinsam, ein Teil möchte man je nach Modus ändern. Lokal möchte man einen bestimmten Port und ein gesprächigeres Verhalten, in einem anderen Modus dann ein anderes. Und man möchte dies unbedingt ohne Kopieren der gesamten Datei lösen.
Stell dir vor, dein catalog-service läuft auf deinem Laptop, auf dem Laptop eines Kommilitonen und auf einem Übungsserver. Der Code ist derselbe, aber die Umstände sind unterschiedlich. Also können auch die Einstellungen abweichen. Wenn du anfängst, für jeden Fall eine „Kopie der application.yaml“ zu erstellen, bekommst du schnell drei fast identische Dateien und eine ewige Suche nach der Frage „in welcher habe ich title geändert?“. Wir brauchen eine Möglichkeit, eine Basis zu behalten und kleine „Aufkleber“ darauf zu setzen.
Und genau hier taucht eine gute Technik auf: Multi-Document-YAML. Eine physische Datei kann mehrere YAML-Dokumente enthalten. Jedes Dokument ist ein eigener „Teil“ von Einstellungen. Spring Boot kann sie lesen und, was wichtig ist, bestimmte Dokumente nur für ein bestimmtes Profil aktivieren.
2. Multi-Document-YAML und „Schichten“
Multi-Document-YAML ist kein „Spring-Magie“. Es handelt sich um eine Fähigkeit von YAML selbst: Innerhalb einer Datei können mehrere Dokumente durch eine Zeile --- getrennt sein. Spring Boot liest eine solche Datei als mehrere separate Konfigurationsquellen und setzt dann das endgültige Wertebild zusammen. Das ist wie mehrere transparente Folien, die übereinander gelegt werden.
Ein wichtiger Punkt zum Verständnis: Jedes Dokument ist ein eigenständiges YAML-Objekt. Es hat seine eigenen Stamm-Schlüssel (spring, server, app…), seine eigene Verschachtelung und muss nicht die Struktur des vorherigen Dokuments wiederholen. Als Ergebnis kann man die Basiseinstellungen im ersten Dokument schreiben und im zweiten nur das angeben, was sich ändert.
Ein minimales Beispiel, das genau die Idee der „Schichten“ zeigt (vorerst ohne Profile):
app:
catalog:
title: "Spring+ Catalog" # Basiswert (erste Schicht)
---
app:
catalog:
title: "Spring+ Catalog (override)" # überschreibt nur das, was sich ändert
Wenn beide Dokumente „aktiv“ sind, ist die Logik einfach: Der spätere Wert überschreibt den früheren. Am Ende wird app.catalog.title zu "Spring+ Catalog (override)". Dieses Verhalten ähnelt dem normalen Leben: Wenn du zuerst sagst „Treffen um 18:00 Uhr“ und dann „Ach, machen wir 19:00 Uhr“, gewinnt die zweite Nachricht, auch wenn die erste schöner war.
Um ein geistiges Modell zu festigen, ist es praktisch, es sich so vorzustellen:
flowchart TD
A["application.yaml"] --> B["Dokument 1: base config"]
A --> C["Dokument 2: extra config"]
B --> D["Zusammenstellung der Endwerte"]
C --> D
D --> E["Ergebnis: spätere Werte überschreiben frühere"]
Bisher ist das einfach „mehrere Dokumente“. Aber der eigentliche Clou beginnt, wenn wir lernen, ein Dokument nicht immer, sondern nur für einen bestimmten Modus zu aktivieren.
3. Profile und on-profile
Profil: Bedeutung in dieser Lektion
Das Wort „Profile“ in Spring Boot klingt leicht nach etwas Großem und Beängstigendem, aber heute brauchen wir es in einem sehr einfachen Sinne. Ein Profil ist der Name eines Konfigurationsmodus, der der Anwendung mitteilt: „Gehe davon aus, dass wir in diesem Kontext starten“. Zum Beispiel local – „Ich starte auf meinem Laptop“, dev – „Lehr-Dev-Modus“.
Wichtig ist derzeit nur ein Mechanismus: das bedingte YAML-Dokument, das für einen bestimmten Modus aktiviert wird. Woher das Profil selbst aktiviert wird – aus der Datei, einer Umgebungsvariable oder einem Startargument – ist vorerst nebensächlich; zuerst muss man die Mechanik des Overlays sehen, sonst kann man Profile leicht mit einer Kopie der gesamten Konfiguration verwechseln.
Uns geht es jetzt darum, eines zu verstehen: Ein Profil ist kein „noch eine Datei“, kein „noch ein Projekt“ und keine „noch eine Version der Anwendung“. Der Code bleibt derselbe. Es ändern sich nur einige Werte der Einstellungen. Ein Profil ist ein Schalter, der es der Anwendung ermöglicht, das richtige Stück Konfiguration auszuwählen.
Spring Boot kann ein oder mehrere Profile aktiv haben. Für unseren ersten Schritt reicht eines aus: Wir denken so, als ob bei jedem Start ein Modus ausgewählt wird. Das hilft, nicht in Details zu ertrinken und den Nutzen sofort zu sehen.
Und noch eine wichtige Einschränkung genau für diese Lektion: Wir verwandeln Profile nicht in eine ganze Infrastrukturgeschichte. Wir erzeugen das „erste profilabhängige Verhalten“ direkt in einer Datei application.yaml, sauber und ohne überflüssige Mechanismen.
spring.config.activate.on-profile: Dokument nach Modus aktivieren
Jetzt kommt das Interessanteste: Wie sagt man Spring Boot, dass „dieses Dokument nur für das Profil local angewendet werden soll“? Dazu schreibt man innerhalb des Dokuments, das bedingt sein soll, einen Block:
spring:
config:
activate:
on-profile: local # Dokument wird nur bei aktivem Profil local aktiviert
Und dann – die normalen Einstellungen, die nur in diesem Modus angewendet werden sollen.
Ein Minibeispiel: Der Basistitel ist allgemein, aber in local ein etwas anderer (z. B. damit du mit den Augen siehst, dass der lokale Modus gestartet wurde):
app:
catalog:
title: "Spring+ Catalog" # Basis: wird immer angewendet
---
spring:
config:
activate:
on-profile: local # Bedingung: dieses Dokument nur für local
app:
catalog:
title: "Spring+ Catalog (local)" # Patch auf die Basis (ändert nur den Titel)
Hier passiert etwas Wichtiges. Das zweite Dokument ist nicht immer an der endgültigen Konfiguration beteiligt. Es wird nur dann aktiviert, wenn das Profil local aktiv ist. Wenn das Profil ein anderes ist oder nicht gesetzt wurde, wird das zweite Dokument ignoriert, und der Basiswert aus dem ersten Dokument bleibt erhalten.
Beachte den Stil: Das zweite Dokument ändert genau einen Wert, wiederholt aber nicht alles. Dies ist der Schlüssel, damit Multi-Document-YAML nicht zur Kopierpaste wird.
Um das Bild noch klarer zu machen, kann man den Zusammenbau der Konfiguration als „Filtern von Dokumenten“ darstellen:
flowchart TD
S["Start der Anwendung"] --> R["Lese application.yaml"]
R --> D1["Dokument 1: ohne Bedingung -> wird immer angewendet"]
R --> D2["Dokument 2: on-profile=local"]
R --> D3["Dokument 3: on-profile=dev"]
D2 -->|wenn local aktiv| M["Merge"]
D2 -->|wenn nicht local| X["Ignorieren"]
D3 -->|wenn dev aktiv| M
D3 -->|wenn nicht dev| X
D1 --> M
M --> F["Endgültige Werte"]
Das bedeutet, Profile sind einfach ein Filter, der entscheidet, welches Dokument „sichtbar“ und welches „unsichtbar“ ist.
4. Beispiel für catalog-service
Lass uns das nun auf unser Projekt anwenden. Wir haben bereits einen lebenden catalog-service: Er startet, bringt einen eingebetteten Server hoch und kann JSON-Endpunkte ausliefern. Daher ist es praktisch, den Profileffekt über eine Einstellung zu zeigen, die sofort sichtbar ist, ohne neuen Java-Code schreiben zu müssen. Der naheliegendste Kandidat ist server.port: In den Startlogs siehst du sofort, auf welchem Port Tomcat hochgefahren ist.
Machen wir es so: Basisbereit wird der Service auf 8080 laufen, im Profil local auf 8081. Parallel dazu bereiten wir unsere app.catalog.*-Schlüssel vor, die bereits in der Konfiguration leben (auch wenn der Code sie erst etwas später ausliest).
Unten – eine kombinierte Demo: Die Basisdatei bleibt dieselbe, es wird einfach ein local-Dokument hinzugefügt. Zur Verdeutlichung ist local direkt in der Datei aktiviert, sodass der Profil-Patch sofort an Port und Logzeilen sichtbar ist.
Beispiel für eine kompakte src/main/resources/application.yaml:
spring:
application:
name: catalog-service
profiles:
active: local # demo: local direkt hier aktiviert, um den Profil-Patch sofort zu sehen
server:
port: 8080 # Basisport (falls das Profil local nicht aktiv ist)
app:
catalog:
title: "Spring+ Catalog" # Basistitel
maintenance-mode: false # Basisflag (im Profil nicht geändert)
---
spring:
config:
activate:
on-profile: local # dieses Dokument wird nur im Modus local angewendet
server:
port: 8081 # local-Überschreibung des Ports
app:
catalog:
title: "Spring+ Catalog (local)" # local-Kennzeichnung, um den Modus mit den Augen zu sehen
Beachte, dass hier local nur zur Veranschaulichung direkt im Basisdokument aktiviert ist: So ist der Profil-Patch sofort an Port und Titel zu erkennen. Der Sinn dieser Demonstration ist: Du änderst nur die Konfiguration, und das Startverhalten (Port und Werte) ändert sich.
Wenn du jetzt die Anwendung startest, siehst du in den Startup-Logs zwei nützliche Signale. Erstens schreibt Spring Boot normalerweise, welches Profil aktiv ist. Zweitens wird die Zeile zum Port eine andere sein.
Ein Ausschnitt des Logs wird ungefähr so aussehen (stark gekürzt, um die Lektion nicht zum Lesen einer Textwand zu machen):
... The following 1 profile is active: "local"
... Tomcat started on port(s): 8081 (http)
Das ist unser „erstes profilabhängiges Verhalten“: Derselbe Code, aber der Port und ein Teil der Einstellungen unterscheiden sich je nach Profil.
Und noch ein kleiner, aber wichtiger Punkt: Den app.catalog.title haben wir ebenfalls je nach Profil geändert. Vorerst mag der Code diesen Titel nicht nutzen, aber wir haben bereits das Prinzip gezeigt: Das Basisdokument gibt die allgemeine Bedeutung vor, das Profildokument eine punktuelle „Korrektur“.
Um klar zu machen, was sich genau geändert hat, ist es hilfreich, die endgültigen Werte in einer Tabelle darzustellen:
| Schlüssel | Basis-Dokument | Dokument on-profile: local | Ergebnis bei active=local |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
nicht gesetzt | |
Die Tabelle zeigt die Hauptregel: Das Profildokument sollte nur das ändern, was sich tatsächlich unterscheidet. Alles andere bleibt basisch.
5. Stil von Profil-Dokumenten ohne Kopierpaste
Wenn Menschen zum ersten Mal von Profilen erfahren, haben sie eine sehr verständliche Versuchung: „Aha, also erstelle ich ein Dokument für local, in dem alles gleich ist, nur ein bisschen anders“. Dann ein gleiches Dokument für dev. Dann für „Ich-mama-prod“. Und plötzlich hört die Konfiguration auf, ein System zu sein: Sie wird zu einer Sammlung von Kopien, die mit der Zeit auseinanderlaufen und die Vorhersagbarkeit zerstören.
Die richtige „Ingenieurhaltung“ für Multi-Document-YAML ist: Das erste Dokument ist die Basis, Profil-Dokumente sind „Patches“. Ein Patch ist immer eine kleine Änderung auf der Basis, keine neu geschriebene Basis.
Vergleichen wir den schlechten und den guten Stil anhand eines kurzen Beispiels.
Schlechter Stil (Kopierpaste der Basis in das Profil-Dokument, wodurch später niemand mehr weiß, wo die Wahrheit liegt):
app:
catalog:
title: "Spring+ Catalog"
maintenance-mode: false
---
spring:
config:
activate:
on-profile: local
app:
catalog:
title: "Spring+ Catalog (local)"
maintenance-mode: false # sinnlose Wiederholung: die Basis gibt das bereits vor
Guter Stil (im Profil nur das, was sich tatsächlich ändert):
app:
catalog:
title: "Spring+ Catalog"
maintenance-mode: false
---
spring:
config:
activate:
on-profile: local
app:
catalog:
title: "Spring+ Catalog (local)" # Patch: ändert nur das, was sich tatsächlich unterscheidet
In der „guten“ Variante ist die Konfiguration später einfacher zu warten. Wenn du den Standardwert für maintenance-mode ändern möchtest, änderst du ihn genau an einer Stelle. Das Profil-Dokument widerspricht nicht der Basis und erzeugt keine „versteckte Wahrheitskopie“.
Ein weiterer kleiner Trick, der hilft, den Kopf klar zu behalten: Versuche, das Profil-Dokument mit einem Satz zu beschreiben. Zum Beispiel: „Im local-Modus ändern wir den Port und fügen eine Markierung im Titel hinzu“. Wenn das Profil-Dokument aussieht wie „da ist alles ganz anders“, erstellst du wahrscheinlich keine „Modus-Konfiguration“ mehr, sondern eine alternative Realität. Das kann auch manchmal nötig sein, aber sicher nicht auf unserem Niveau und nicht jetzt.
6. Fallstricke von YAML: Einrückungen, Trennzeichen und Typen
YAML ist heimtückisch, weil es aussieht wie „einfach schön formatierter Text“, aber in Wirklichkeit eine Datenstruktur ist. Ein Fehler von einem Leerzeichen ändert die Bedeutung. Und Multi-Document-YAML fügt noch eine Sache hinzu: Jetzt kannst du nicht nur bei den Einrückungen, sondern auch an der Grenze zwischen Dokumenten einen Fehler machen.
Das häufigste Problem ist, das --- zu vergessen. Dann denkst du, du hast zwei Dokumente geschrieben, aber in Wirklichkeit ist es ein großes Dokument, und dein spring.config.activate.on-profile wird zu einem ganz normalen Einstellungsblock ohne Effekt des „Ein-/Ausschaltens“.
Der zweite häufige Fehler ist eine falsche Verschachtelung von on-profile. In Spring Boot 4 ist die korrekte Position genau diese: spring -> config -> activate -> on-profile. Wenn du versehentlich in der Einrückung „verrutschst“, kann Boot die Aktivierungsbedingung des Dokuments nicht erkennen, und das Dokument wird entweder „immer aktiv“ oder ignoriert (je nachdem, was genau du kaputt gemacht hast).
Zum Beispiel sieht das so verdächtig ähnlich aus, ist aber aufgrund der Einrückung bereits falsch:
spring:
config:
activate:
on-profile: local # Fehler: on-profile ist aus activate herausgerutscht (Einrückung zerstört)
Und so ist es korrekt:
spring:
config:
activate:
on-profile: local # korrekt: on-profile in activate eingebettet
Der dritte Fallstrick sind die Werttypen. YAML kann Zeichenketten in Zahlen, true/false in Booleans umwandeln, und das ist oft nützlich. Aber manchmal benötigst du ausdrücklich eine Zeichenkette. Wenn du zum Beispiel "8081" in Anführungszeichen schreibst, ist es eine Zeichenkette, ohne Anführungszeichen ist es eine Zahl. Für server.port ist eine Zahl in Ordnung. Für bestimmte „Labels“, Daten und Ähnliches ist es besser, bewusst Anführungszeichen zu verwenden, um nicht zu raten, was YAML interpretiert hat.
Und zuletzt: Kommentare. Ein Kommentar in YAML beginnt mit #. Das ist praktisch, wenn du erklären möchtest, warum ein Profil aktiviert ist. Vergiss aber nicht, dass der Kommentar Teil der Zeile ist. Ein Leerzeichen vor # ist guter Stil, damit die Konfiguration wie Text und nicht wie ein „Zauberspruch“ aussieht.
7. Typische Fehler bei der Arbeit mit Profilen
Fehler Nr. 1: Dokument ist „bedingt“, aber die Bedingung ist an der falschen Stelle.
Am häufigsten passiert das wegen falscher Einbettung. Es sieht mit dem Auge so aus, als sei on-profile „irgendwo in der Nähe“, aber für YAML sind Leerzeichen die Bedeutung. Das Profil-Dokument beginnt dann, immer oder nie angewendet zu werden, und der Entwickler vermutet, dass „Boot kaputt ist“. Normalerweise reicht es, die Einrückungen auszurichten und zu überprüfen, ob der Schlüsselpfad genau spring.config.activate.on-profile ist.
Fehler Nr. 2: Das Profil-Dokument kopiert vollständig das Basis-Dokument.
Auf kurze Distanz sieht das „klar“ aus: du öffnest das Dokument local und siehst alle Einstellungen. Aber in der Praxis führt das zur Divergenz der Kopien. Nach ein paar Änderungen wirst du in die Situation kommen, dass max-featured-count in der Basis geändert wurde, aber vergessen wurde, es in local zu ändern, und die Anwendung verhält sich „einfach so unterschiedlich“. Das Profil-Dokument sollte ein kleiner Patch sein.
Fehler Nr. 3: In einer Datei wird sofort alles geändert, einschließlich „gemeinsamer“ Schlüssel, und dann versteht man das Ergebnis nicht.
Multi-Document-YAML funktioniert wie das Überlagern von Schichten. Wenn du in der Basis 20 Schlüssel vorgibst und dann im Profil noch 20 Schlüssel (ein Teil davon widersprüchlich), wird das Ergebnis schwer im Kopf zu behalten sein. Am Anfang wird das durch Disziplin geheilt: Das Profil-Dokument sollte 1–3 Schlüssel ändern, die man leicht aufzählen kann.
Fehler Nr. 4: Man erwartet, dass das Profil „sich selbst aktiviert“, obwohl es nirgends gesetzt wurde.
Ein bedingtes Dokument wird nur dann aktiviert, wenn das Profil tatsächlich aktiv ist. Wenn das Profil nicht aktiviert ist, wird das Dokument nicht angewendet – und das ist normal. Für die erste Begegnung ist es am einfachsten, das Profil vorübergehend direkt in der application.yaml zu aktivieren, wie im Beispiel. So sieht man die Mechanik des Multi-Document-YAML leichter, ohne sie mit verschiedenen Arten der Profilaktivierung zu vermischen.
Fehler Nr. 5: Man versucht, Profile als Weg zu nutzen, um Geschäftslogik zu verstecken.
Manchmal möchte man Folgendes machen: „Im Profil dev soll ein Algorithmus laufen, im Profil prod ein anderer“. Auf Konfigurationsebene kann das funktionieren, aber es ist bereits eine architektonische Weggabelung, die das Projekt schnell verkompliziert. Im Rahmen unseres catalog-service sind Profile jetzt genau das: Einstellung des Verhaltens und der Parameter, nicht das Umschalten von „Services auf ein anderes Gehirn“.
GO TO FULL VERSION