CodeGym /Kurse /Spring Boot /WebApplicationType und die Wahl des Startmodus

WebApplicationType und die Wahl des Startmodus

Spring Boot
Level 5, Lektion 2
Verfügbar

1. Die Rolle von WebApplicationType: nicht nur Web

Wir haben bereits gesehen, dass SpringApplication.run(...) mehrere Phasen durchläuft. Eine der frühesten ist herauszufinden, welchen Anwendungstyp Boot überhaupt hochfährt. Solange diese Entscheidung nicht gefallen ist, ist unklar, ob ein Webserver gebraucht wird, welcher Kontext erstellt werden soll und warum in den Logs entweder ein Port erscheint oder nicht.

Wenn du aus der Welt „Backend = HTTP" zu Spring Boot kommst, tappst du leicht in eine Falle: Es scheint, als müsse jede Boot-Anwendung ein Webservice sein. In der Praxis ist Spring Boot eine Plattform, die verschiedene Anwendungstypen starten kann: von Konsolen-Utilities bis hin zu Webservices. Damit der Start keine Raterei wird, trifft Boot früh eine Entscheidung: welchen „Typ" von Anwendung wir gerade hochfahren. Diese Entscheidung beeinflusst, welcher Kontext erstellt wird, ob ein Webserver startet, ob ein Netzwerkport erscheint und welche Web-Infrastruktur überhaupt angemessen ist.

Stell dir vor, Boot ist ein Manager am Flughafen, der dein Ticket sieht und entscheidet, wohin er dich führt. Wenn du „ohne Gepäck und ohne Stewardess" reist (das entspricht NONE), brauchst du kein riesiges Terminal mit Gepäckband und Passkontrolle. Wenn du einen „normalen Flug" buchst (das entspricht SERVLET), wird das Terminal gebraucht, sonst kommst du nicht ins Flugzeug. Und wenn du „einen anderen Flugtyp" buchst (das entspricht REACTIVE), brauchst du einen anderen Gate und andere Regeln. Wichtig ist nicht, welche Variante „besser" ist, sondern dass sie unterschiedlich sind und Boot eine wählen muss — sonst baut es die Anwendung aus „inkompatiblen Teilen".

Der praktische Grund, WebApplicationType zu verstehen, ist ganz einfach: Du wirst Abhängigkeiten hinzufügen — Starter —, und manchmal ändert eine einzige Zeile in build.gradle.kts den Charakter der Anwendung plötzlich. Gestern war das ein stilles Konsolen-Utility, heute ist es ein Webservice mit Port. Oder umgekehrt: Du dachtest, du hast einen Service, aber er startet ohne Server, weil der Web-Stack nicht im Classpath ist oder du ihn versehentlich ausgeschlossen hast. Das Verständnis von WebApplicationType macht solche Situationen nicht zur Mystik, sondern zu einem lesbaren technischen Ursache-Wirkungs-Zusammenhang.

2. Werte von WebApplicationType

Wenn Boot von „Web-Anwendungstyp" spricht, philosophiert es nicht. Es wählt buchstäblich einen Wert des Enums WebApplicationType, von dem auf unserem Level drei Varianten wichtig sind. Und hier ist es nützlich, sofort das Wichtigste festzuhalten: Das ist keine „Controller-Einstellung" und keine „Annotation auf einer Klasse". Das ist der Modus der gesamten Anwendung, der den Start insgesamt beeinflusst.

Zur Klarheit fassen wir die Unterschiede in einer kleinen Tabelle zusammen. Nicht als Nachschlagewerk für alle Fälle, sondern als „Landkarte", damit du dich nicht verlierst.

WebApplicationType Was das in der Praxis bedeutet Wird ein Port geöffnet? Typischer Abhängigkeits-Stack (Idee) Wo am häufigsten anzutreffen
NONE Die Anwendung startet keinen Webserver und wartet nicht auf HTTP-Anfragen Nein „normales Boot ohne Web-Starter" Konsolen-Utilities, Batch, Migrationen, Services ohne HTTP
SERVLET Die Anwendung funktioniert als klassische Servlet-Webanwendung (unser Kurs) Ja spring-boot-starter-webmvc normale REST/HTTP-Services, MVC, klassisches Backend
REACTIVE Die Anwendung läuft im reaktiven Web-Modus Ja spring-boot-starter-webflux Projekte mit WebFlux (in diesem Kurs — nur wissen, dass es existiert)

Jetzt ein wichtiger praktischer Punkt. Für catalog-service halten wir uns an den SERVLET-Modus und bringen spring-boot-starter-webmvc in den Classpath. Der reaktive Modus (REACTIVE) ist hier nur als dritter Name in der Liste nötig — damit du nicht denkst, Spring Boot „schreibt manchmal zufällig REACTIVE in die Logs". Nein, das ist kein Zufall: Das ist der gewählte Modus.

Und NONE ist eine eigene nützliche Idee: Spring Boot als Plattform erfordert kein Web. Wenn du irgendwann ein kleines Utility zur Datenvorbereitung, Konfigurationsprüfung oder Berichtserstellung bauen möchtest, kannst du den Spring-Kontext und DI nutzen — alles, was wir schätzen — aber ohne Server und Port.

Und noch etwas, damit keine falschen Assoziationen entstehen. REACTIVE ist kein „beschleunigter Modus", sondern ein anderes Web-Runtime-Modell. Wenn du diesen Wert in einer Anwendung siehst, bedeutet das nicht „Boot hat beschlossen, schneller zu werden", sondern „du oder der Classpath haben einen anderen Web-Stack mitgebracht".

3. Auswahl von WebApplicationType anhand des Classpaths

Das Geheimnis der Auswahl von WebApplicationType ist gleichzeitig langweilig und wunderschön: Spring Boot trifft die Entscheidung auf Basis des Classpaths, also der Bibliotheken, die der Anwendung tatsächlich zur Verfügung stehen. Das ist dieselbe Philosophie wie bei Boot insgesamt: „Abhängigkeiten bestimmen Fähigkeiten, Fähigkeiten bestimmen Standardwerte". Und das ist sehr logisch, weil Boot beim Start noch keine deiner Controller, keine deiner Services, keine deine „innere Welt" kennt — es gibt nur die Startklasse und das, was in den Abhängigkeiten liegt.

Am einfachsten sieht man das an dem bereits bekannten Einflusspunkt — build.gradle.kts. Auch ohne tiefes Gradle-Verständnis ist es nützlich, eine Regel im Kopf zu behalten: Starter sind nicht nur „Bibliotheken für die Kompilierung", sondern Schalter für den Lebensmodus der Anwendung.

Hier ein Beispiel (ein Ausschnitt), der die Idee gut veranschaulicht. Nicht damit du jetzt alles umstrukturierst, sondern damit du den Ursache-Wirkungs-Zusammenhang siehst.

dependencies {
    // Basis-Starter: Autokonfiguration, Logging und die sonstige „Basis" von Spring Boot
    implementation("org.springframework.boot:spring-boot-starter")

    // Servlet-Stack (führt Boot in der Regel zu WebApplicationType.SERVLET)
    implementation("org.springframework.boot:spring-boot-starter-webmvc")

    // Reactive-Stack (führt Boot in der Regel zu WebApplicationType.REACTIVE) — im Kurs nicht verwendet
    // implementation("org.springframework.boot:spring-boot-starter-webflux")
}

Wenn der Servlet-Web-Stack im Classpath vorhanden ist — insbesondere der Starter spring-boot-starter-webmvc und seine transitiven Abhängigkeiten —, wählt Boot fast immer SERVLET. Wenn webmvc fehlt, aber der Reactive-Web-Stack (webflux) vorhanden ist, wählt es REACTIVE. Wenn weder das eine noch das andere vorhanden ist — NONE.

Für Einsteiger ist das ein sehr wichtiger Gedanke: Du änderst das Verhalten der Anwendung nicht nur durch Code, sondern auch durch die Liste der Abhängigkeiten. Und Spring Boot tut das bewusst. Das ist kein „Chaos", sondern Plattformlogik.

Damit das noch greifbarer wird, hier ein kleines Flussdiagramm im Stil „Boot stellt deinem Classpath Fragen auf einem Vorstellungsgespräch". Die Details der Prüfklassen sind jetzt nicht wichtig; wichtig ist die Entscheidungsstruktur selbst.

flowchart TD
    A["SpringApplication startet"] --> B{"Ist Servlet-Web-Stack im Classpath?"}
    B -- ja --> S["SERVLET"]
    B -- nein --> C{"Ist Reactive-Web-Stack im Classpath?"}
    C -- ja --> R["REACTIVE"]
    C -- nein --> N["NONE"]

Es gibt eine subtile Nuance, die man im Voraus kennen sollte, damit man später nicht überrascht wird: Wenn du irgendwie beide Stacks mitgebracht hast — Servlet und Reactive —, muss Boot sich für eines entscheiden. In vielen Fällen wählt es SERVLET als die „klassischere" Standardoption. Aber sieh das nicht als Einladung, alles auf einmal mitzubringen, sondern als Warnung: Das Mischen von Stacks ohne Verständnis führt leicht zu unerwarteten Effekten. In unserem Lernprojekt vermeiden wir das und halten uns an einen Web-Stack.

4. Kontext und Server

Wenn du „Anwendungstyp" hörst, liegt die Frage nahe: „Na und? Was macht das aus, wenn ich sowieso @SpringBootApplication habe?" Es macht einen Unterschied, und er ist sehr praktisch: Der gewählte Modus bestimmt, welcher ApplicationContext erstellt wird und ob überhaupt ein Webserver gestartet wird.

Die Reihenfolge ist hier entscheidend: Zuerst bestimmt SpringApplication den WebApplicationType, dann wählt es danach den konkreten ApplicationContext, und nur im Web-Zweig ergibt es überhaupt Sinn, einen Embedded Server hochzufahren. Das heißt, der Server ist schon eine Folge des gewählten Modus, keine separate Entscheidung irgendwo am Ende des Starts.

Auch ohne die genauen Klassennamen auswendig zu kennen, ist es nützlich, die Idee zu verstehen. Im SERVLET-Modus erstellt Spring Boot einen weborientierten Kontext, der mit der Servlet-Umgebung umgehen und einen Embedded Server starten kann. Im NONE-Modus wird ein gewöhnlicher Nicht-Web-Kontext erstellt, der DI und Konfiguration wunderbar beherrscht, aber keine Web-Infrastruktur aufbaut. Im REACTIVE-Modus — ein reaktiver Web-Kontext, der für einen anderen Stack ausgelegt ist.

Auf der Gefühlsebene macht sich das ganz einfach bemerkbar. Im SERVLET-Modus siehst du in den Logs typischerweise Hinweise darauf, dass der Server hochgefahren ist und einen Port lauscht — zum Beispiel die Erwähnung von Tomcat und dem Port. Im NONE-Modus gibt es solche Zeilen nicht: Die Anwendung startet, der Kontext wird zusammengebaut, aber niemand öffnet einen Port, weil es nichts und niemanden gibt, der das tun müsste.

Noch eine nützliche Perspektive: WebApplicationType ist die frühe Entscheidung von Boot darüber, in welchem „Ökosystem" es die Anwendung zusammenbauen wird. Wenn NONE gewählt wird, wird die Web-Infrastruktur nicht einfach „ungenutzt" bleiben, sondern oft überhaupt nicht erstellt oder deaktiviert. Und das ist normal: Boot schleppt nicht gern Unnötiges mit.

Manchmal versuchen Einsteiger so zu denken: „Ich habe doch eine Controller-Klasse hinzugefügt, also ist die Anwendung web." Aber in Boot ist die Kausalität umgekehrt: Zuerst wird der Startmodus bestimmt — unter anderem anhand der Abhängigkeiten —, dann wird darunter die Infrastruktur hochgefahren, und erst dann werden deine Komponenten „eingeschraubt". Controller allein zwingen Boot nicht, plötzlich eine Webanwendung zu werden, wenn kein Web-Stack vorhanden ist.

5. Expliziter Modus über setWebApplicationType(...)

Die automatische Moduserkennung ist praktisch, aber manchmal braucht man explizite Kontrolle. Und Spring Boot bietet diese auf sehr einfache Weise: Du kannst ein SpringApplication-Objekt erstellen, den WebApplicationType setzen und dann run(...) aufrufen. Das ist genau die „explizite Startform", die wir bereits in der letzten Vorlesung gesehen haben, nur dass wir sie jetzt nicht der Schönheit halber nutzen, sondern für eine konkrete Start-Konfiguration.

Das CatalogServiceApplication selbst bleibt dabei dasselbe. Für Lernexperimente ändert sich nur, wie wir SpringApplication innerhalb von main() zusammenbauen und starten.

Zuerst — ein Beispiel, das die Anwendung ohne Webserver startet, auch wenn die Abhängigkeiten einen Web-Stack enthalten. Das ist nützlich als Lerndemo: Ein und dasselbe Projekt kann sich unterschiedlich verhalten.

SpringApplication app = new SpringApplication(CatalogServiceApplication.class);

// Webserver zwangsweise deaktivieren (kein Port wird geöffnet), auch wenn der Web-Stack in den Abhängigkeiten vorhanden ist
app.setWebApplicationType(WebApplicationType.NONE);

// Anwendung starten: der Kontext wird hochgefahren, aber Web-Infrastruktur wird nicht erstellt
app.run(args);

Das ist keine neue Standardform von catalog-service, sondern eine temporäre explizite Überschreibung, die es ermöglicht, den Unterschied zwischen den Modi an ein und derselben Anwendung zu spüren.

Wenn du catalog-service so startest, startet er als Spring-Anwendung — der Kontext ist vorhanden —, aber als Webservice kommt er nicht hoch: kein Port. Und dieses Gefühl „Boot ohne Web" ist sehr nützlich, das im Kopf zu behalten: Boot ist keine „Hülle um Tomcat", sondern eine Plattform zum Zusammenbauen von Anwendungen.

Jetzt — die kanonische Variante für unser Projekt: der SERVLET-Modus.

SpringApplication app = new SpringApplication(CatalogServiceApplication.class);

// Explizit den Servlet-basierten Modus angeben (normaler Webservice in unserem Kurs)
app.setWebApplicationType(WebApplicationType.SERVLET);

// Anwendung starten: Embedded Server wird hochgefahren, Port erscheint, Web-Infrastruktur wird aktiviert
app.run(args);

Dieser Code ist streng genommen meistens überflüssig: Wenn du spring-boot-starter-webmvc hast, wählt Boot sowieso SERVLET. Aber als Lerndemo ist er nützlich: Du siehst, dass der Modus ein separater Parameter ist, keine „Magie rund um Controller".

Und schließlich — nur für das Gesamtbild, nicht für die Praxis in diesem Kurs — die dritte Variante:

SpringApplication app = new SpringApplication(CatalogServiceApplication.class);

// REACTIVE setzen, ohne Reactive-Abhängigkeiten mitzubringen (z. B. webflux), führt zu Problemen beim Start
app.setWebApplicationType(WebApplicationType.REACTIVE);

// Versuche, im Reactive-Modus zu starten, nur als Demo: so sieht man, dass das ein echter Modusschalter ist
app.run(args);

Der Sinn dieses Beispiels liegt nicht darin, dass du jetzt den Reactive-Modus startest, sondern darin, dass du verstehst: REACTIVE ist ein echter Modusschalter, kein „Wort aus einem trendigen Vortrag". Wenn du REACTIVE setzt, aber der benötigte Reactive-Stack im Classpath fehlt, wird die Anwendung höchstwahrscheinlich nicht glücklich sein. Boot kann nicht „so tun als ob" es eine Webanwendung wäre ohne Web-Abhängigkeiten.

6. Experiment: Modus NONE ohne Web

Es ist sehr leicht, den falschen Schluss zu ziehen: „Wenn ich Web ausgeschaltet habe, dann funktioniert Spring nicht mehr." Nein. Im NONE-Modus hast du nach wie vor ApplicationContext, DI, Component Scan, @Configuration, @Bean und alle grundlegenden Mechanismen. Es fehlt nur der Webserver und alles, was mit der Verarbeitung von HTTP-Anfragen zu tun hat.

Um das mit den eigenen Händen und Augen zu erleben, fügen wir dem Projekt eine winzige Marker-Komponente hinzu. Sie hat nichts mit Business-Logik zu tun, sie gibt uns nur eine einfache Tatsache: Die Komponente wurde gefunden, der Bean wurde erstellt.

package com.example.catalogservice.catalog;

import org.springframework.stereotype.Component;

@Component
public class CatalogMarker {

    // Einfache „Marker"-Methode: nur um zu sehen, dass der Bean wirklich erstellt und verfügbar ist
    public String name() {
        return "catalog-service";
    }
}

Jetzt ändern wir vorübergehend main() desselben CatalogServiceApplication so, dass wir die Anwendung im NONE-Modus starten, den Kontext holen und den Bean herausnehmen. In einem echten Projekt würden wir das nicht tun: Normalerweise werden Beans nicht manuell aus main() geholt. Aber als „Röntgenaufnahme" zum Verständnis des Starts ist das eine ausgezeichnete Technik.

SpringApplication app = new SpringApplication(CatalogServiceApplication.class);
app.setWebApplicationType(WebApplicationType.NONE);

// Spring starten und den Kontext holen (er ist auch ohne Webserver vorhanden)
ConfigurableApplicationContext context = app.run(args);

// Bean manuell aus dem Kontext holen — als diagnostischer Trick für das Lernbeispiel
CatalogMarker marker = context.getBean(CatalogMarker.class);

// Ergebnis ausgeben: bedeutet, dass Component Scan funktioniert hat und der Bean erstellt wurde
System.out.println(marker.name()); // catalog-service

Was beweist du dir damit — und deinem inneren Skeptiker, der überzeugt ist, dass Spring nur für Web lebt? Dass der Spring-Container hochgefahren ist, Component Scan funktioniert hat, der Bean erstellt und verfügbar ist. Das heißt, der gesamte „Kern" der Anwendung ist vorhanden. Boot hat nur den Web-Modus nicht aktiviert, weshalb die Anwendung keinen Netzwerkzugang über HTTP hat.

Und jetzt stell dir vor, wie sehr das hilft, die Gedanken zu ordnen. Wenn du eine Anwendung startest und siehst, dass deine Beans erstellt werden, aber kein Port erscheint, ist das keine Panik mehr: „Nichts funktioniert." Das ist eine Frage: „In welchem Modus bin ich gestartet und warum?" Und WebApplicationType ist genau die Antwort auf diese Frage.

7. Symptome des Startmodus

Nach dieser Vorlesung wirst du eine ganze Klasse von seltsamen Situationen leichter erklären können, die normalerweise aussehen wie „Spring macht wieder Magie". In der Praxis ist es fast immer eines von mehreren Szenarien, die mit dem Startmodus zusammenhängen.

Das häufigste Symptom — du erwartest, dass die Anwendung einen Port öffnet, zum Beispiel 8080, aber der Port erscheint nicht. Die Ursache liegt oft nicht darin, dass „Tomcat kaputt ist", sondern darin, dass die Anwendung als NONE gestartet hat — entweder wegen der Abhängigkeiten oder weil jemand den Modus explizit gesetzt hat. In diesem Fall muss der Server nicht hochfahren, und die Logik „ich prüfe den Controller" gilt einfach nicht: Kein Web-Runtime — keine Anfragen.

Das umgekehrte Symptom kommt ebenfalls vor: Du wolltest ein kleines Utility, aber die Anwendung startet plötzlich als Webservice und „lärmt" in den Logs über den Server. Das passiert oft, wenn man nach Gewohnheit einen Web-Starter „für alle Fälle" ins Projekt aufgenommen hat. Boot hat ehrlich den Web-Stack im Classpath gesehen und SERVLET gewählt. Das ist kein Fehler von Boot — das ist sein logisches Verhalten.

Ein weiteres Symptom — Verwechslung zwischen Servlet- und Reactive-Modus. Manchmal ziehen Entwickler versehentlich sowohl webmvc als auch webflux ins Projekt — über verschiedene Starter, manchmal transitiv. Boot muss einen Modus wählen, und die Wahl kann nicht mit den Erwartungen übereinstimmen. Für einen Einsteiger sieht das aus wie „es startet, aber irgendwie nicht richtig". Mit dem Verständnis von WebApplicationType ist das schon ein anderes Gespräch: „Welcher Stack ist wirklich im Classpath und welcher Modus wurde letztendlich gewählt?"

Man kann sogar eine kurze Diagnose-Regel formulieren. Wenn das Startverhalten nicht mit den Erwartungen übereinstimmt, ist es nützlich, sich drei Fragen zu stellen, bevor man tiefer gräbt. Ist überhaupt ein Web-Stack in den Abhängigkeiten? Ist WebApplicationType nicht explizit gesetzt? Und stimmt der Startmodus mit dem überein, wie du das Projekt grundsätzlich siehst — als Utility oder als Service? Diese Fragen sparen oft stundenlange Arbeit, weil du anfängst, die Ursache am richtigen Ort zu suchen: im „Anwendungstyp", nicht in zufälligen Details.

8. Typische Fehler beim Umgang mit WebApplicationType

Fehler Nr. 1: Denken, dass „Boot = Web" ist, und den Startmodus ignorieren.
Das führt zu sehr seltsamen Debugging-Sessions: Du suchst das Problem in Controllern und Ports, obwohl die Anwendung als NONE gestartet hat. Besser ist es, einen einfachen Gedanken im Kopf zu behalten: Boot ist eine Plattform, und Web ist einer der Modi. Wenn kein Port vorhanden ist, kann das kein „Defekt" sein, sondern der gewählte Modus.

Fehler Nr. 2: Glauben, dass der Modus durch das Vorhandensein von @RestController bestimmt wird — oder generell „durch meinen Code".
In Boot sind der Classpath und die frühen Start-Einstellungen primär, nicht die Klassen, die du geschrieben hast. Ein Controller ohne Web-Stack ist nur eine Klasse mit einer Annotation, die in diesem Modus fehl am Platz sein kann. Wenn du Web willst, stelle sicher, dass der Web-Stack wirklich vorhanden ist.

Fehler Nr. 3: Zwangsweise WebApplicationType.REACTIVE setzen, ohne zu verstehen, dass das ein anderer Stack ist.
Einsteiger lesen manchmal „reactive" als „fortgeschritten" oder „schnell" und versuchen, es überall zu aktivieren. In der Praxis ist das ein anderes Runtime und ein anderes Abhängigkeits-Ökosystem. REACTIVE ohne die entsprechenden Abhängigkeiten zu setzen ist ungefähr so, als würde man versuchen, ein Auto anzulassen, indem man Benzin in den USB-Anschluss füllt: Beides ist „in eine Öffnung", aber der Effekt ist ein anderer.

Fehler Nr. 4: Versehentlich zwei Web-Stacks mitbringen und sich über die „Unvorhersehbarkeit" des Starts wundern.
Wenn die Abhängigkeiten gleichzeitig Servlet- und Reactive-Web enthalten, muss Boot einen Modus wählen. Ohne Verständnis von WebApplicationType fühlt sich das wie Chaos an. Das Gegenmittel ist einfach: Den Web-Stack des Projekts sauber halten und Starter bewusst auswählen, nicht „für alle Fälle" sammeln.

Fehler Nr. 5: setWebApplicationType(...) als „dauerhaften Krückstock" statt als bewusstes Werkzeug verwenden.
Manchmal versteht ein Entwickler nicht, warum die Anwendung „nicht richtig" startet, und beginnt, den Modus in main() zwangsweise als magisches Pflaster zu setzen. Das funktioniert, verbirgt aber die Ursache. Viel nützlicher ist es, zunächst zu verstehen, warum Boot den einen oder anderen Modus gewählt hat — Classpath und Abhängigkeiten —, und dann zu entscheiden, ob eine explizite Überschreibung nötig ist.

1
Aufgabe
Spring Boot, Level 5, Lektion 2
Gesperrt
Expliziter `NONE`-Modus selbst mit web starter
Expliziter `NONE`-Modus selbst mit web starter
1
Aufgabe
Spring Boot, Level 5, Lektion 2
Gesperrt
Expliziter Modus `SERVLET`
Expliziter Modus `SERVLET`
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION