Start / Seminare / Spring Boot 4 Grundlagen
Modul
Konfigurationsverwaltung und Profile
4 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
Konfigurationsverwaltung und Profile
0:00 Jede Anwendung, die es über den eigenen Rechner hinaus schafft, braucht Einstellungen, die sich unterscheiden. Die Datenbank ist lokal eine andere als im Test und wieder eine andere in Produktion. Klingt banal, ist aber die Stelle, an der überraschend viele Projekte hässlich werden — mit Konfigurationsdateien, die nachträglich ins Archiv gepatcht werden, oder mit Bauläufen je Umgebung.
0:22 In diesem Modul sehen wir, wie Spring Boot das löst. Der Kern ist ein einziger Gedanke: Konfiguration kommt von außen, aus vielen möglichen Quellen, und diese Quellen stehen in einer festen Rangfolge. Wer diese Rangfolge kennt, spart sich später viel Sucherei.
Konfigurationsverwaltung und Profile
0:38 Wir gehen in drei Stufen vor. Zuerst das Einfache: eine Datei im Projekt, ein Wert, ausgelesen im Code. Dann der eigentlich wichtige Teil — die Erkenntnis, dass diese Datei nur eine von vielen Quellen ist und keineswegs die stärkste. Danach schauen wir uns Profile an, mit denen sich ganze Sätze von Einstellungen umschalten lassen. Zum Schluss ein kurzer Ausblick darauf, wie das unter Kubernetes aussieht.
1:03 Vorweg die Beruhigung: In der Anwendung ändert sich dafür nichts.
Eigenschaften setzen und lesen
1:07 Fangen wir mit dem naheliegendsten Fall an: eine Datei im Projekt, ein Schlüssel, ein Wert. Das ist der Einstieg, den jeder kennt — und gleichzeitig der Fall, der später am wenigsten trägt. Aber der Mechanismus dahinter bleibt derselbe. Neben dem Quellcode liegt im Projekt ein Verzeichnis für Ressourcen, und was dort liegt, wandert beim Bauen mit ins Jar.
1:30 Dort steht auch die Datei application.properties, im klassischen Java-Format: je Zeile ein Schlüssel, ein Gleichheitszeichen, ein Wert. Beim Hochfahren liest Spring Boot diese Datei ein und stellt die Werte der Anwendung zur Verfügung. So weit, so unspektakulär — und genau so soll es sein. Der interessante Teil ist nicht das Format, sondern die Frage, wer diese Werte später überschreiben darf. Behalten Sie das im Kopf, wir kommen im nächsten Kapitel darauf zurück.
1:59 Auf dieser Folie stehen zwei sehr verschiedene Sorten von Einstellungen, und das ist der Punkt. Die ersten beiden sind vorgegeben: Spring Boot und seine Starter definieren hunderte solcher Schlüssel, mit denen sich Verhalten steuern lässt — etwa der Port, auf dem der eingebettete Tomcat lauscht. Die dritte ist unsere eigene, frei erfunden für Radwerk. Beide funktionieren nach demselben Muster. Bleibt die Frage, woher man die vorgegebenen kennt.
2:24 Die Antwort steht in der Fußzeile: Die Referenzdokumentation führt sie vollständig auf, und die Entwicklungsumgebung schlägt sie beim Tippen samt Beschreibung und Standardwert vor. Jetzt zum Auslesen. Die Annotation Value funktioniert ähnlich wie eine Injektion, nur wird kein anderer Bean eingesetzt, sondern ein konfigurierter Wert.
2:44 Wichtig ist der zweite Teil: Nach einem Doppelpunkt lässt sich ein Vorgabewert angeben. Das klingt nach einem Detail, ist aber eine Grundsatzentscheidung. Ohne Vorgabewert und ohne gesetzte Eigenschaft startet die Anwendung nämlich gar nicht. Das kann man wollen — bei einem Datenbankkennwort ist ein sofortiger Abbruch besser als ein stiller Fehlstart. Bei einem Begrüßungstext wäre es albern.
3:07 Sie entscheiden das also je Eigenschaft, und zwar bewusst. Zwei Dinge lohnen hier den Blick. Das erste ist die Schreibweise mit Doppelpunkt: Findet sich kein Wert, greift der Text dahinter. Das zweite ist die Mechanik drumherum. Die Klasse setzt eine Schnittstelle um, deren Methode Spring Boot beim Start automatisch aufruft — für jeden Bean, der sie umsetzt.
3:29 Das ist ein bequemer Haken für alles, was einmalig beim Hochfahren passieren soll. Und der Protokollierer daneben braucht keine eigene Abhängigkeit; die Protokollbibliothek kommt über die Starter ohnehin mit. Wieder so ein Fall, in dem man etwas benutzen kann, ohne es angefordert zu haben. Der zweite Punkt auf dieser Folie ist der eigentlich wertvolle: Der Fehler tritt beim Hochfahren auf, nicht erst im Betrieb.
3:53 Das ist ein Muster, das Sie in Spring Boot immer wieder finden werden, und es ist eine bewusste Entscheidung. Eine Anwendung, die wegen einer fehlenden Einstellung gar nicht erst startet, ist unangenehm — aber deutlich weniger unangenehm als eine, die drei Stunden später beim ersten Zugriff eines Kunden zusammenbricht. Übertragen heißt das: Ein Vorgabewert ist kein Bequemlichkeitsmerkmal, sondern eine Aussage darüber, ob diese Einstellung wirklich zwingend ist.
4:19 Bei dieser Aufgabe gibt es zwei Teile, und der zweite ist der wichtigere. Zuerst definieren Sie eine eigene Einstellung, lesen sie aus und lassen sie beim Start protokollieren. Dann aber entfernen Sie die Eigenschaft wieder und prüfen, ob Ihre Anwendung trotzdem hochkommt. Genau daran zeigt sich, ob Sie den Rückfallwert verstanden haben.
4:38 Wer noch Zeit hat: Benennen Sie die Datei nach YAML um und schreiben Sie dieselben Werte im YAML-Format. Beide Formate funktionieren gleichwertig — aber pflegen Sie im Projekt nur eines davon, sonst weiß bald niemand mehr, wo ein Wert steht. Der zweite Punkt ist der, den man leicht übersieht: eigene Schlüssel unter das Präfix spring zu legen. Das wirkt ordentlich, ist aber ein Namensraum, der dem Framework gehört.
5:03 Über kurz oder lang kollidiert Ihr Schlüssel mit einem echten, und die Suche danach ist ausgesprochen unerfreulich. Wählen Sie ein eigenes Präfix — bei uns radwerk. Der dritte Punkt ist eine Hygienefrage: Properties- und YAML-Datei nebeneinander zu pflegen, funktioniert technisch, führt aber verlässlich dazu, dass jemand einen Wert an der falschen Stelle ändert und sich über die Wirkungslosigkeit wundert.
Externalisierte Konfiguration
5:26 Jetzt kommt der Teil, um dessentwillen dieses Modul existiert. Bis hier war Konfiguration etwas, das mit ins Archiv gebacken wird. Ab jetzt wird sie zu etwas, das von außen kommt — und das ändert die Perspektive grundlegend. Denken Sie kurz zu Ende, was es bedeuten würde, wenn die Datei im Jar die einzige Quelle wäre. Für jede Umgebung müssten Sie neu bauen.
5:49 Damit wäre das Artefakt, das Sie testen, ein anderes als das, was in Produktion läuft — und genau diese Gleichheit ist einer der Gründe, warum man überhaupt containerisiert. Also ist die Datei im Jar nur eine Quelle unter vielen. Spring Boot liest ebenso Dateien aus dem Dateisystem, Umgebungsvariablen, Java-Systemeigenschaften und Kommandozeilenargumente. Die Anwendung selbst merkt davon nichts.
6:13 Sie fragt nach einem Wert und bekommt ihn. Und hier steht der Satz, den ich Ihnen für den Betrieb mitgeben möchte. Alle Quellen werden in einer festen Reihenfolge verarbeitet, und diese Reihenfolge entscheidet, wer wen übersteuert. Das klingt nach einer Fußnote, ist aber die praktische Konsequenz aus allem davor. Denn daraus folgt eine Diagnoseregel: Wenn Sie einen unerwarteten Wert sehen, ist das fast nie ein Fehler im Code. Es ist eine stärkere Quelle, die Sie übersehen haben.
6:42 Ein Kollege hat eine Umgebungsvariable gesetzt, ein Startskript übergibt ein Argument. Suchen Sie also nicht im Programm, sondern in der Kette. Die Logik dieser Reihenfolge ist gut nachvollziehbar, wenn man sie von unten liest: Je näher eine Quelle am konkreten Lauf ist, desto stärker wiegt sie. Ganz unten steht die Datei im Jar — sie gilt für alle Läufe überall und ist deshalb die schwächste. Darüber die Datei im Dateisystem, die zu einer bestimmten Umgebung gehört.
7:10 Dann die Umgebungsvariable, die zu diesem einen Container gehört. Und ganz oben das Kommandozeilenargument, das nur für diesen einen Start gilt. Das Prinzip heißt: Je spezifischer, desto stärker. Wenn Sie sich das merken, brauchen Sie die Tabelle nicht. Was Sie hier sehen, ist ein kleines Experiment, das ich Ihnen dringend ans Herz lege: dieselbe Eigenschaft aus drei Quellen setzen und beobachten, welche gewinnt.
7:36 Erst dann sitzt die Rangfolge wirklich. Auf ein Detail möchte ich hinweisen, weil es regelmäßig Verwirrung stiftet — die Schreibweise. In der Datei steht der Schlüssel klein mit Punkten, als Umgebungsvariable groß mit Unterstrichen. Das ist keine Inkonsequenz, sondern Rücksicht auf die Konventionen des Betriebssystems. Spring Boot übersetzt zwischen beiden Formen.
7:57 Wer die Umgebungsvariable trotzdem klein schreibt, wundert sich anschließend, warum nichts passiert. Der entscheidende Schritt ist der dritte, und der Unterschied zwischen den beiden Varianten ist größer, als die Namen vermuten lassen. Die eine Variante ersetzt die Datei aus dem Jar vollständig — alles, was dort stand und in Ihrer neuen Datei fehlt, ist damit weg.
8:18 Die andere ergänzt sie nur, und Ihre Werte haben Vorrang. In der Praxis wollen Sie fast immer die ergänzende Variante, weil sie die Vorgaben aus dem Projekt erhält und nur das Abweichende beschreibt. Die ersetzende Variante ist für Sonderfälle da, in denen Sie wirklich bei null anfangen wollen. Der erste Punkt ist genau der Fall, den ich gerade beschrieben habe, und er passiert häufig aus Unkenntnis der zweiten Option.
8:42 Das Ergebnis ist eine Anwendung, der plötzlich Einstellungen fehlen, die nie jemand angefasst hat. Der zweite Punkt ist der Schreibweisenfehler. Und der dritte fasst die Diagnoseregel dieses Kapitels zusammen: Bei einem unerwarteten Wert im Code zu suchen, ist verlorene Zeit. Gehen Sie stattdessen die Rangfolge durch, von der stärksten Quelle nach unten.
9:02 In Modul sechs lernen wir übrigens einen Endpunkt kennen, der Ihnen genau diese Frage direkt beantwortet.
Profile für Umgebungen
9:08 Einzelne Werte umzuschalten ist das eine. Meist will man aber ganze Sätze von Einstellungen gemeinsam wechseln — alles, was zur Testumgebung gehört, gegen alles, was zur Produktion gehört. Dafür gibt es Profile. Hier ist die wichtigste Information gleich der erste Satz: Ein Profil ist nichts weiter als ein Etikett. Es gibt keine vordefinierten Namen, keine eingebaute Bedeutung, kein Profil, das automatisch etwas Bestimmtes tut.
9:35 Das überrascht viele, die erwarten, dass ein Profil namens prod von sich aus strenger wäre. Ist es nicht. Es ist ein Wort, an das Sie Konfiguration hängen. Üblich sind Namen für Umgebungen, weil das der häufigste Anwendungsfall ist — lokal eine Testdatenbank, in Produktion die echte. Aber Sie könnten Profile genauso für einzelne Funktionen verwenden.
9:57 Das Namensschema ist die ganze Mechanik: der Dateiname, ein Bindestrich, der Profilname. Entscheidend ist, was die Fußzeile sagt — die Profildatei kommt zusätzlich zur schlichten Datei, sie ersetzt sie nicht. Daraus ergibt sich das Arbeitsprinzip für dieses Kapitel: Was in allen Umgebungen gleich ist, steht einmal in der schlichten Datei. In den Profildateien steht nur, was abweicht.
10:20 Das hält die Dateien kurz und, viel wichtiger, es macht die Unterschiede zwischen den Umgebungen auf einen Blick sichtbar. Genau die will man ja sehen können. Aktiviert wird ein Profil über eine ganz gewöhnliche Eigenschaft — und damit gilt für sie alles, was wir im vorigen Kapitel gelernt haben. Sie können sie in eine Datei schreiben, als Umgebungsvariable setzen oder wie hier auf der Kommandozeile übergeben.
10:45 Der zweite Aufruf zeigt, dass mehrere Profile gleichzeitig aktiv sein können. Bei Umgebungsprofilen ergibt das selten Sinn — Test und Produktion zugleich ist keine sinnvolle Kombination. Bei Profilen, die einzelne Funktionen zuschalten, ist es dagegen naheliegend. Das ist der Moment, in dem sich zeigt, dass ein Profil eben wirklich nur ein Etikett ist.
11:06 Die ersten drei Punkte beschreiben eine Aufräumleistung, und die ist im Alltag mehr wert, als sie klingt. Wenn jede Umgebung ihre eigene vollständige Konfigurationsdatei hat, driften diese Dateien auseinander — jemand ändert einen gemeinsamen Wert und vergisst zwei von vier Dateien. Der vierte Punkt öffnet noch eine andere Tür: Die Annotation Profile lässt sich auch an Beans setzen. Damit existiert eine Komponente nur, wenn ein bestimmtes Profil aktiv ist.
11:32 Das ist praktisch für Attrappen in der Entwicklung — und es ist ein Werkzeug, mit dem man auch viel Unheil anrichten kann, wenn man es zu großzügig einsetzt. Der Kern dieser Aufgabe steht im Erfolgskriterium, und zwar in drei Worten: dieselbe Jar-Datei. Es geht nicht darum, zwei Konfigurationen zu haben — es geht darum, dass ein und dasselbe gebaute Artefakt sich in zwei Umgebungen unterschiedlich verhält, ohne neu gebaut zu werden.
11:58 Genau das ist der Sinn der ganzen Übung. Achten Sie beim Aufteilen darauf, wirklich nur das Abweichende in die Profildateien zu schreiben. Wer noch mag, versieht zusätzlich einen Bean mit der Profil-Annotation und beobachtet im Protokoll, wann er auftaucht und wann nicht. Der erste Punkt ist der Verfallsmechanismus, den ich eben beschrieben habe: Sobald alle Werte in jeder Profildatei stehen, ist der Vorteil dahin.
12:22 Der zweite ist der schwerwiegendste — das aktive Profil fest ins Jar zu schreiben. Damit haben Sie die Auslieferung wieder umgebungsabhängig gemacht und den ganzen Aufwand dieses Moduls zunichtegemacht. Das Profil wird beim Start gesetzt, nicht beim Bauen. Und der dritte Punkt betrifft die Erwartungshaltung: Ein Profilname allein tut nichts. Wenn keine Datei und keine Annotation darauf verweist, ist es ein Wort ohne Wirkung.
Konfiguration unter Kubernetes
12:48 Zum Abschluss ein Blick über den Tellerrand. Viele Anwendungen laufen heute unter Kubernetes, und die Plattform verwaltet Konfiguration selbst. Die gute Nachricht vorweg: Für Ihre Anwendung ändert das erstaunlich wenig. Das ist der beruhigende Teil dieses Kapitels. In der Anwendung ist nichts Kubernetes-spezifisches nötig — kein Adapter, keine Bibliothek, keine Fallunterscheidung.
13:11 Der Grund liegt in dem, was wir vorhin gelernt haben: Die Plattform reicht Werte als Umgebungsvariablen oder als Datei in den Container, und beides sind Quellen, die Spring Boot ohnehin liest. Ihre Anwendung weiß nicht, ob eine Umgebungsvariable von Kubernetes, von einem Startskript oder von Ihrer Shell kommt. Diese Gleichgültigkeit ist keine Schwäche, sondern genau der Punkt: Sie macht dieselbe Anwendung überall lauffähig.
13:36 Erzählt als Ablauf klingt das so: Sie bauen Ihre Anwendung ganz normal, ohne an die Zielplattform zu denken. Dann verpacken Sie sie in ein Container-Abbild — wie das geht, sehen wir in Modul sieben. Anschließend beschreiben Sie in Kubernetes-Ressourcen, wie die Anwendung ausgerollt wird und wie sie erreichbar ist. Die Einstellungen kommen in eine eigene Ressource, die ConfigMap.
13:58 Und der letzte Schritt ist der Übergabepunkt: Kubernetes blendet diese Werte als Umgebungsvariablen oder als Datei in den Container ein. Ab dort greift wieder alles, was Sie in diesem Modul gelernt haben. Der erste Punkt ist der einzige in diesem Modul, bei dem ich wirklich eindringlich werde. Eine ConfigMap ist nicht für Vertrauliches gedacht — sie ist im Wesentlichen eine Textdatei in der Plattform, die viele Leute einsehen können.
14:23 Datenbankkennwörter und Schlüssel gehören in ein Secret. Das kostet Sie keine zusätzliche Arbeit in der Anwendung, weil es auf demselben Weg ankommt. Es ist schlicht eine Frage der Disziplin. Der dritte Punkt ist die Wiederkehr unseres Themas: Auch hier gilt die Rangfolge, und auch hier kann eine ConfigMap von einem Argument übersteuert werden.
Was Sie aus Modul 2 mitnehmen
14:43 Der erste Satz ist der wichtigste: Konfiguration ist ein Betriebsthema, kein Buildthema. Wer sie beim Bauen festlegt, hat für jede Umgebung ein anderes Artefakt — und damit die Gleichheit verloren, auf die es ankommt. Die Rangfolge der Quellen ist Ihr Diagnosewerkzeug bei jedem unerwarteten Wert. Profile trennen Gemeinsames von Umgebungsspezifischem und halten die Unterschiede sichtbar.
15:05 Und Vertrauliches gehört in ein Secret, nicht in eine ConfigMap. Im nächsten Modul wird Radwerk endlich fachlich: Wir bauen die REST-Schnittstelle, mit Controllern, Fehlerbehandlung und Validierung.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →