CodeGym /Kurse /Spring Boot /DevTools und die Grenzen des Neustarts

DevTools und die Grenzen des Neustarts

Spring Boot
Level 19, Lektion 2
Verfügbar

1. Neustartgrenze und überflüssige Neustarts

Wenn DevTools aktiviert ist, bemüht es sich, gut zu sein: Es erkennt Änderungen und reagiert. Aber es hat keine Telepathie: Es weiß nicht, dass Sie nur den Text in README.md korrigiert haben und nicht den Code der Geschäftslogik geändert haben. Daher kann es sein, dass es die Anwendung „zu eifrig“ neu startet, und genau hier entsteht die Notwendigkeit, die Pfade zu verwalten – damit ein Neustart nur dann erfolgt, wenn er wirklich nötig ist.

Stellen Sie sich vor, Ihr catalog-service ist ein Café. Restart bedeutet, das Café zu schließen, alle Gäste rauszuwerfen, die Kaffeemaschine neu einzuschalten, die Speisekarte neu zusammenzustellen, das Schild wieder aufzuhängen und die Türen zu öffnen. Manchmal ist das gerechtfertigt (Sie haben die Speisekarte oder die Küche geändert), aber wenn Sie nur einen Tippfehler auf dem Schild „Eingang“ korrigiert haben, ist es ein sehr seltsames Geschäftsmodell, das Café für fünf Minuten zu schließen.

Technisch gesehen sieht das Problem so aus: DevTools überwacht Änderungen an Dateien, die in seinen „Beobachtungsbereich“ fallen (normalerweise der classpath des Projekts). Jede Änderung dort kann potenziell einen restart auslösen. Unser Ziel ist es, die Grenze vorhersagbar zu machen: welche Änderungen einen restart auslösen, welche einen reload und welche ganz ignoriert werden.

2. Standardmäßige Änderungen von DevTools

Die Standardpolitik von DevTools zu verstehen, ist wie die Verkehrsregeln zu kennen. Man kann auch ohne leben … aber dann wundert man sich ständig, warum alle hupen. DevTools orientiert sich in erster Linie an Änderungen im classpath: Dort landen kompilierte Klassen und Ressourcen (src/main/resources), die IDE/Gradle in die Build-Ausgabe kopieren. Genau diese Änderungen werden meist als „ausreichend wichtig“ betrachtet.

Verknüpfen wir das mit der Realität unseres Projekts. Im catalog-service gibt es drei „Arten“ von Änderungen. Die erste Art: Java-Code (Controller, Services, Repository, Konfigurationsklassen). Die Zweite: Konfiguration (application.yaml, application-local.yaml, catalog-data.yaml usw.). Die Dritte: Ressourcen, die sich nicht direkt auf Beans auswirken, z. B. static/index.html. Und es gibt eine vierte, „lästige“ Kategorie: Dateien, die in den Ressourcen liegen, aber nicht von der Anwendung verwendet werden (Dokumentation, Vorlagen, .http-Dateien) – sie können versehentlich einen Neustart auslösen und einem das Leben schwer machen.

Nachfolgend eine kleine „Karte“ für den Kopf (keine absolute Wahrheit, sondern eine praktische Orientierung).

Was Sie geändert haben Wo es normalerweise liegt Sollte dies einen Neustart auslösen? Warum
*.java src/main/java → build/classes Fast immer ja Wiring, Beans, Logik ändert sich
application-*.yaml, catalog-data.yaml src/main/resources → build/resources Meistens ja Konfigurationsbindung erfolgt beim Start
static/index.html src/main/resources/static Normalerweise nein (wünscht reload) Dies ist das „Schaufenster“, nicht der „Motor“
docs/** src/main/resources/docs Nein Beeinträchtigt die Anwendung nicht, kann aber einen Neustart auslösen

Wegen der letzten Zeile lernen wir, die Pfade zu verwalten: Sonst führt DevTools ehrlich einen restart für Änderungen durch, die das Verhalten des Dienstes überhaupt nicht ändern sollten.

DevTools hat dafür drei verschiedene Einstellmöglichkeiten für den restart. Man sollte sie nicht mechanisch alle auf einmal in eine application-local.yaml packen: Der normale local-Modus kann ganz ohne sie auskommen, und die Einstellung wählt man je nach konkreter Quelle überflüssiger Neustarts oder nach einem zu überwachenden externen Verzeichnis aus.

3. spring.devtools.restart.exclude: Vollständige Kontrolle

Manchmal möchte man DevTools sagen: „Liebes, ich entscheide selbst, was wichtig ist und was nicht.“ Dafür gibt es spring.devtools.restart.exclude. Aber dieser Knopf hat seinen Charakter: Er fügt nicht „sauber hinzu“, sondern ersetzt die Liste der Ausnahmen. Das heißt, Sie übernehmen die Rolle der Person, die nun für die Neustartregeln insgesamt verantwortlich ist. Das ist in Ordnung, aber nur, wenn Sie bereit sind, sich später selbst zu erklären, warum plötzlich alles seltsam läuft.

Im Studienprojekt wird exclude meistens nicht benötigt, um „es nach eigenen Vorstellungen zu machen“, sondern um die Mechanik zu demonstrieren. Im echten Leben verwendet ein Anfänger oft exclude wie einen Hammer: „Mein Gerät startet zu oft neu – also schließe ich alles aus.“ Dann wundert er sich, warum er den Code ändert, die Anwendung sich aber scheinbar nicht ändert. An dieser Stelle entstehen normalerweise Legenden über „fehlerhaftes Spring“.

Ein minimales Beispiel (streng als Illustration der Mechanik) könnte so aussehen:

# src/main/resources/application-local.yaml
spring:
  devtools:
    restart:
      # Vom restart ausschließen, was normalerweise ohne Neuerstellung des Kontexts aktualisiert werden soll.
      # Wichtig: exclude ersetzt die Standardausnahmen von DevTools, nicht sie zu ergänzen.
      exclude: "static/**,public/**"

Hier sagen wir explizit: Änderungen in static/** und public/** sollen keinen Neustart auslösen. Aber ein wichtiger Punkt: Wenn DevTools zuvor noch andere Dinge ausgeschlossen hat (z. B. andere Resource-Ordner), könnte man diese bei diesem Ansatz „versehentlich vergessen“, und das Verhalten ändert sich.

Daher sollte man exclude besser als Modus „Ich stelle die Regeln komplett selbst ein“ verstehen. Er ist nützlich, wenn Sie genau wissen, was Sie tun, oder wenn Sie das Verhalten maximal kontrollierbar und für alle im Team einheitlich gestalten möchten (und bereit sind, das zu pflegen).

4. spring.devtools.restart.additional-exclude

Wenn exclude bedeutet „altes Haus abreißen und neu bauen“, dann bedeutet additional-exclude „ein weiteres Regal anbringen und die tragenden Wände nicht anrühren“. In den meisten Lern- und kleinen Projekten ist genau das nötig: DevTools hat bereits sinnvolle Standardeinstellungen, und Sie fügen einige wenige Ausnahmen für Ihr Repository hinzu, um unnötige Neustarts zu vermeiden.

Im catalog-service klingt ein typischer Fall so: „Ich habe in meinem Projekt einen Ordner mit Dokumentation oder Anfragedateien, und ich möchte nicht, dass das Bearbeiten dieser Dateien die Anwendung neu startet.“ Nehmen wir an, Sie speichern einige Lernmaterialien in src/main/resources/docs/ (architektonisch nicht die beste Lösung, aber im Leben kommt es vor).

Dann ist es sinnvoller, Folgendes zu tun:

# src/main/resources/application-local.yaml
spring:
  devtools:
    restart:
      # Wir fügen eigene Ausnahmen hinzu, ohne die Standardregeln von DevTools zu ändern.
      # Hier schützen wir uns vor unnötigen Neustarts aufgrund von Dokumentation und .http-Dateien.
      additional-exclude: "docs/**,**/*.http"

Der Sinn ist klar: Die Standardausnahmen von DevTools bleiben erhalten, und Sie fügen zwei eigene hinzu. Und genau hier zeigt sich eine wichtige ingenieurtechnische Eigenschaft: Sie können erklären, warum diese Ausnahmen hinzugefügt wurden. Das ist immer ein guter Test für die Angemessenheit der Konfiguration.

Dadurch wird Ihre Feedback-Schleife gleichmäßiger: Sie können in Ruhe Dokumentation und Sample-Requests bearbeiten, ohne alle 20 Sekunden einen Neustart des eingebetteten Tomcat zu beobachten (der langsam den Verdacht schöpft, als Geisel gehalten zu werden).

5. Pfadmuster für exclude und additional-exclude

Die Pfade in exclude und additional-exclude sind eine kleine Mustersprache. Und wie bei jeder kleinen Mustersprache verursacht nicht die Komplexität die größten Schmerzen, sondern die Selbstüberschätzung. Man denkt: „Ich habe * geschrieben – das deckt alles ab.“ Und plötzlich wird etwas anderes ausgeschlossen als erwartet, und DevTools verhält sich wie eine Katze: Sieht einen an und tut so, als sei nichts passiert.

In der Boot-Umgebung trifft man am häufigsten auf eine Ant-Stil-Logik mit Platzhaltern. Die Idee ist einfach: Sie beschreiben eine „Maske“ für Dateien, deren Änderungen keinen Neustart auslösen sollen. In der Konfiguration schreibt man normalerweise mehrere durch Kommas getrennte Masken. Wichtig: Leerzeichen in der Zeichenkette werden manchmal Teil des Werts, daher ist es besser, sauber ohne „schöne“ Leerzeichen zu schreiben.

Nachfolgend eine kleine Bedeutungstabelle, damit das Gehirn nicht jedes Mal das Rad neu erfinden muss:

Muster Intuitive Bedeutung Praktisches Beispiel
static/** alles innerhalb von static/ in beliebiger Tiefe static/index.html, static/css/app.css
docs/** der gesamte Dokumentationsordner docs/guide.md, docs/img/logo.png
**/*.http alle .http-Dateien überall http/catalog-service.http (falls auf dem classpath)
public/** alles in public/ ähnlich wie static/

Vorsicht ist geboten: Wenn Sie ein zu breites Muster schreiben (z. B. **/* oder **/*.yaml), könnten Sie versehentlich den Neustart für wichtige Dinge wie Konfiguration oder Klassen deaktivieren, und dann werden Sie nicht die Anwendung debuggen, sondern Ihre eigene DevTools-Konfiguration. Das ist eine ziemlich Meta-Erfahrung, kommt aber meistens nicht wie geplant und bereitet keine Freude.

6. spring.devtools.restart.additional-paths: Externe Ordner

Bislang haben wir hauptsächlich über das gesprochen, was „innerhalb des Projekts“ liegt und auf den classpath gelangt. Aber unser catalog-service lebt bereits in einer Welt der externalisierten Konfiguration: Wir können Konfigurationen nicht nur aus src/main/resources lesen, sondern auch aus externen Dateien, z. B. aus ./config/ neben dem JAR/Projekt. Und genau hier könnte DevTools ohne Hinweis die Änderung „nicht bemerken“: Die Datei wurde geändert, aber ein Neustart erfolgt nicht, weil DevTools sie nicht überwacht.

spring.devtools.restart.additional-paths ist so, als würde man DevTools sagen: „Hier ist ein weiterer Bereich, den du im Auge behalten musst.“ Dies ist in unserem Kurs besonders naheliegend, weil Sie bereits spring.config.import, spring.config.additional-location und die Idee externer Konfigurationen kennen.

Ein Konfigurationsbeispiel sieht einfach aus:

# src/main/resources/application-local.yaml
spring:
  devtools:
    restart:
      # Weisen DevTools an, den externen Ordner zu überwachen (neben dem Projekt/Start).
      # Wenn sich darin eine Datei ändert, kann DevTools einen Neustart initiieren.
      additional-paths: "./config"

Wenn Sie nun eine Datei in ./config bearbeiten, kann DevTools die Änderung erkennen und einen restart auslösen. Und ein Neustart ist genau das, was wir brauchen, weil die Konfiguration (über @ConfigurationProperties) beim Start des Kontexts „erfasst“ wird. Ohne Neustart sehen Sie normalerweise keine neuen Werte und denken, dass YAML Sie ignoriert (obwohl Sie eigentlich nur den Kontext nicht neu gestartet haben).

Wenn die gesamte Konfiguration innerhalb des Projekts lebt und Sie nichts im externen Verzeichnis überwachen müssen, ist additional-paths hier einfach nicht nötig. Es ist ein separates Werkzeug für ein spezifisches Szenario, keine obligatorische Ergänzung zu den exclude-Einstellungen.

Um das Thema nicht im luftleeren Raum zu lassen, verknüpfen wir alles zu einem „verständlichen Bild“ speziell für den catalog-service. Gemäß Projektspezifikation haben wir eine externe Konfiguration, z. B. ./config/catalog-extra.yaml. Damit die Anwendung eine solche beliebig benannte Datei überhaupt liest, ist es einfacher, sie explizit über den bereits bekannten spring.config.import zu importieren. Und damit DevTools auf Änderungen im selben Ordner reagiert, fügen wir additional-paths hinzu.

Im lokalen Profil könnte das so aussehen (kurz und bündig):

# src/main/resources/application-local.yaml
spring:
  config:
    # Binden die externe Datei als Konfigurationsquelle ein.
    # Für einen beliebigen Dateinamen ist hier import praktischer.
    import: "optional:file:./config/catalog-extra.yaml"
  devtools:
    restart:
      # Lassen DevTools diesen selben Ordner überwachen (allein die Tatsache der Änderungsverfolgung).
      additional-paths: "./config"

Und die externe Datei selbst kann einen Teil unserer app.catalog.*-Einstellungen überschreiben, z. B. den Titel (zur Erinnerung: CatalogProperties – typed config model):

# ./config/catalog-extra.yaml
app:
  catalog:
    # Beispiel eines Werts, der ohne Neubau des Projekts geändert werden kann.
    # Damit es jedoch angewendet wird, ist dennoch ein Neustart des Kontexts erforderlich.
    title: "Catalog Service (external config)"

Warum ist diese Verknüpfung methodisch wichtig? Wir beschleunigen nicht nur den Neustart. Wir sorgen dafür, dass Ihr Konfigurationsmodell aus dem vorherigen Modul (Profile, Imports, externe Orte, @ConfigurationProperties) im Dev-Modus ordentlich funktioniert. Sonst ergibt sich die lustige (und sehr verbreitete) Situation: Die Konfiguration haben Sie bereits nach außen verlagert, aber es ist umständlich, sie zu ändern, weil „irgendwas nicht übernommen wird“. DevTools hilft genau dabei, dieses Szenario angenehm zu gestalten, aber nur, wenn Sie die Überwachungsgrenzen richtig beschrieben haben.

7. Regel: Wann ist ein restart erforderlich

In der realen Entwicklung muss man sich selten alle Eigenschaftsnamen merken. Man muss schnell die Frage beantworten können: „Soll diese Änderung den Kontext neu erstellen oder nicht?“ Wenn die Regel zu komplex ist, wird man trotzdem anfangen, nach Gefühl zu handeln, und DevTools wirkt wie „instabile Magie“. Daher ist es hilfreich, einen sehr einfachen Algorithmus im Kopf zu haben.

Nachfolgend ein kleiner Entscheidungsbaum. Er beschreibt nicht, was DevTools tatsächlich in jedem Fall tun wird, sondern wie Sie denken und wie Sie die Einstellungen exclude / additional-exclude / additional-paths auswählen sollten.

flowchart TD
    A["Datei geändert"] --> B{"Beeinflusst es Beans / Wiring / Konfigurationsbindung?"}
    B -- "Ja" --> C["Benötigt restart (Kontext neu erstellen)"]
    B -- "Nein" --> D{"Ist es eine statische Ressource, die einfach nach außen gegeben wird?"}
    D -- "Ja" --> E["Besser ohne restart (Reload/Refresh von Ressourcen)"]
    D -- "Nein" --> F["Wahrscheinlich Rauschen: vom restart ausschließen"]

Übersetzt man das Diagramm in menschliche Sprache, ergibt sich eine einfache Regel. Code oder Konfiguration geändert, von der die Erstellung von Beans und das Verhalten der @ConfigurationProperties abhängt – Neustart erwarten und nicht dagegen ankämpfen. Das „Schaufenster“ geändert (HTML in static) – Aktualisierung ohne Neustart wünschen. Eine Datei geändert, die überhaupt nicht am Betrieb der Anwendung beteiligt ist – ausschließen, damit sie keinen Neustart auslöst.

Diese Regel spart nicht nur Zeit. Sie macht DevTools erklärbar: Sie konfigurieren keine Magie, sondern beschreiben die Grenzen dessen, was für die Neuerstellung des Kontexts wirklich wichtig ist.

8. Typische Fehler bei der Arbeit mit DevTools

Wenn ein Anfänger zum ersten Mal DevTools bekommt, durchläuft er normalerweise drei Phasen: Begeisterung, Frustration, „Okay, lass uns konfigurieren“. Und genau in der Konfigurationsphase entstehen die meisten Fehler, die dann als „DevTools spinnt“ wahrgenommen werden. In der Praxis spinnt fast nie DevTools, sondern unsere eigene Pfadpolitik.

Fehler Nr. 1: exclude verwenden, obwohl additional-exclude ausreicht.
Das sieht harmlos aus: Sie möchten nur eine Ausnahme hinzufügen, aber Sie überschreiben die gesamte Liste. Danach startet DevTools plötzlich bei Änderungen neu, bei denen es vorher nicht neu gestartet hat, oder umgekehrt – es reagiert nicht mehr auf wichtige Ressourcen. Wenn das Ziel darin besteht, „ein paar Ordner hinzuzufügen“, ist fast immer additional-exclude die richtige Wahl, da es die Standardregeln nicht zerstört.

Fehler Nr. 2: Aus einer emotionalen Reaktion heraus zu breite Muster schreiben.
Manchmal sieht jemand, dass die Anwendung zu oft neu startet, und fügt etwas wie **/*.yaml oder sogar **/* hinzu, um DevTools „zu beruhigen“. Eine Minute später ändert er application-local.yaml, aber die Anwendung ändert ihr Verhalten nicht, weil es keinen Neustart mehr gibt. Das erzeugt das Gefühl, Spring „liest die Konfiguration nicht“. In Wirklichkeit liest Spring sie schon, nur haben Sie den Mechanismus deaktiviert, der die Änderung hätte anwenden sollen.

Fehler Nr. 3: additional-paths hinzufügen, aber vergessen, dass die Anwendung diese Dateien auch lesen muss.
additional-paths bedeutet Überwachung, nicht magisches Einbinden der Konfiguration. Wenn DevTools auf ./config achtet, die Datei aber nicht über spring.config.import oder einen anderen geeigneten Mechanismus eingebunden ist, kann die Änderung zwar einen Neustart auslösen, aber das Verhalten der Anwendung wird dadurch nicht beeinflusst: Die Datei ist einfach nicht an der Konfigurationserstellung beteiligt. Und umgekehrt: Wenn die Anwendung eine externe Konfiguration liest, DevTools diese aber nicht überwacht, werden Sie bis zum ersten manuellen Neustart fragen: „Warum wird das nicht übernommen?“

Fehler Nr. 4: Vom Neustart ausschließen, was tatsächlich den Kontext beeinflusst.
Manchmal möchte man, dass catalog-data.yaml „wie eine statische Datei“ ohne Neustart aktualisiert wird. Aber in unserem Kurs wird der Kurskatalog über eine typisierte Konfiguration (@ConfigurationProperties) geladen, daher sollten Änderungen in der YAML-Datei zu einer Neuerstellung des Kontexts führen (oder zumindest zu einem Neustart des Binding-Mechanismus). Wenn Sie diese Datei vom Neustart ausschließen, erzeugen Sie eine falsche Erwartung: „Ich habe die YAML geändert – und alles wurde übernommen.“

Fehler Nr. 5: DevTools-Einstellungen nicht im local-Profil, sondern in der allgemeinen application.yaml speichern.
Dadurch machen Sie lokale Annehmlichkeiten zu globalen Regeln. Wenn Sie später ein anderes Profil aktivieren, das Projekt einem Kollegen geben oder es in einem anderen Modus ausführen, beeinflusst DevTools plötzlich das Verhalten dort, wo es gar nicht gebraucht wird. DevTools ist ein Werkzeug für die lokale Entwicklung, und seine Pfadpolitik sollte lokal und explizit an das local-Profil gebunden sein, damit die Basiskonfiguration des Projekts sauber und portabel bleibt.

1
Aufgabe
Spring Boot, Level 19, Lektion 2
Gesperrt
Ausschluss des docs-Verzeichnisses vom Neustart
Ausschluss des docs-Verzeichnisses vom Neustart
1
Aufgabe
Spring Boot, Level 19, Lektion 2
Gesperrt
Überwachung des externen Verzeichnisses config
Überwachung des externen Verzeichnisses config
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION