Start / Seminare / Quarkus für Spring-Boot-Teams
Modul
Spring Boot und Quarkus im Architekturvergleich
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.
Spring Boot und Quarkus im Architekturvergleich
0:00 Im ersten Modul ging es um die Frage, ob sich ein Wechsel lohnt. Jetzt geht es darum, worin der Unterschied technisch überhaupt besteht. Und der wichtigste Unterschied ist keine Annotation und keine Bibliothek — es ist ein Zeitpunkt. Spring Boot entscheidet beim Start, was die Anwendung enthält. Quarkus entscheidet das beim Bauen.
0:19 Aus dieser einen Verschiebung folgt fast alles andere: die kurze Startzeit, der geringe Speicherbedarf, aber auch die Einschränkungen bei Reflection und bei fremden Spring-Bibliotheken. Wer diesen Zeitpunkt verstanden hat, kann die meisten Überraschungen einer Migration vorhersagen.
Spring Boot und Quarkus im Architekturvergleich
0:36 Bleiben wir noch einen Moment bei der Theorie — sie zahlt sich in den nächsten sechzehn Modulen aus. Wir sehen uns an, was an die Stelle des Application Context tritt, was Build-Time-Processing genau bedeutet und welche Folgen das für Bibliotheken und für die Wahl zwischen imperativem und reaktivem Programmiermodell hat.
0:53 Danach wird es konkret: Ab dem nächsten Modul entsteht ein Projekt.
Application Context und ArC
0:57 Fangen wir beim Herzstück an. In Spring Boot ist es der Application Context, in Quarkus heißt das Gegenstück ArC. Beide verwalten Beans — aber sie tun es zu völlig unterschiedlichen Zeitpunkten. ArC ist die CDI-Implementierung von Quarkus. Sie setzt Jakarta CDI in der schlankeren Ausprägung CDI Lite um und besteht deren Prüfsuite; der volle CDI-Umfang wird bewusst nicht angeboten.
1:22 Das Bild dazu: Der Application Context ist ein Kellner, der Ihre Bestellung entgegennimmt und in der Küche nachfragt, was möglich ist. ArC ist ein Menü, das gedruckt vorliegt, bevor Sie das Lokal betreten. Sie bekommen schneller Ihr Essen — aber Sonderwünsche, die nicht vorgesehen waren, gibt es auch nicht mehr. Diese Gegenüberstellung enthält den ganzen Rest des Moduls im Kleinen.
1:45 Links entsteht der Bean-Graph beim Start und bleibt zur Laufzeit formbar — deshalb funktionieren dort Dinge wie nachträgliche Registrierung. Rechts steht der Graph schon im Artefakt, unveränderlich. Der praktisch wichtigste Unterschied ist die letzte Zeile: Fehler zeigen sich nicht mehr beim ersten Start, sondern im Build.
2:03 Das fühlt sich zunächst strenger an, ist aber die angenehmere Variante — ein roter Build kostet Minuten, ein fehlgeschlagener Produktivstart kostet deutlich mehr. Wenn zur Laufzeit nicht mehr gesucht wird, stellt sich die Frage, woher Quarkus die Beans kennt. Die Antwort heißt Bean Archive: Ihre eigenen Klassen zählen immer dazu, fremde Bibliotheken nur dann, wenn sie einen Index mitbringen oder ausdrücklich nachgemeldet werden.
2:28 Das ist der Grund, warum eine Fremdbibliothek manchmal einfach still bleibt. Zweiter wichtiger Punkt: ArC räumt auf und entfernt Beans, die niemand benutzt. Das spart Speicher — kann aber genau dann stören, wenn eine Bean nur reflektiv verwendet wird. Merken Sie sich beide Mechanismen, sie erklären später zwei klassische Fehlersuchen.
2:48 Alle vier Punkte sind Varianten derselben Überraschung: Etwas funktioniert nicht und sagt nichts. Eine Bean wird entfernt, weil sie scheinbar ungenutzt ist. Eine Bibliothek bleibt unsichtbar, weil ihr der Index fehlt. Ein CDI-Mechanismus, den man aus großen Jakarta-Anwendungen kennt, gehört zum vollen Umfang und ist hier schlicht nicht da.
3:07 Und der Unterschied zwischen normalen Scopes und Pseudo-Scopes wird meist erst beim ersten Proxy-Fehler wirklich verstanden. Die gute Nachricht: Alle vier Fälle treten früh auf, im Build oder beim ersten Start — nicht nach drei Wochen Produktivbetrieb.
Runtime Auto-Configuration gegen Build-Time-Processing
3:22 Damit kommen wir zum Kern des Ganzen. Auto-Configuration kennen Sie aus Spring Boot in- und auswendig. Sehen wir uns an, was in Quarkus an ihre Stelle tritt — und warum das mehr ist als eine Umbenennung. Quarkus teilt den Weg zur laufenden Anwendung in drei Phasen. In der Augmentation liest das Framework beim Bauen die Metadaten und zeichnet Bytecode auf.
3:44 Static Init führt diesen Code sehr früh aus — beim Native Build sogar noch auf der Baumaschine. Runtime Init läuft erst beim Start der fertigen Anwendung. Spring Boot erledigt die entsprechende Arbeit vollständig beim Start. Das Bild: Quarkus liefert ein vorgekochtes Gericht, das nur noch erwärmt wird. Schneller auf dem Tisch — aber was in der Küche entschieden wurde, lässt sich am Tisch nicht mehr ändern.
4:09 Lesen Sie diese Schichtung von oben nach unten als Zeitstrahl. Ganz oben, in der Augmentation, passiert die eigentliche Denkarbeit — und zwar auf Ihrem Build-Server. In der Mitte läuft aufgezeichneter Code, der bewusst nichts darf, was echte Laufzeit braucht: keine Ports öffnen, keine Threads starten. Erst unten kommt das, was wirklich erst beim Start entschieden werden kann — Verbindungsdaten zum Beispiel.
4:33 Und ganz unten steht das Ergebnis: Es werden nur noch die Klassen geladen, die tatsächlich gebraucht werden. Genau daraus entsteht die kurze Startzeit, nicht aus einem Trick. Diese Tabelle beantwortet eine Frage, die früh gestellt wird: Ist eine Extension einfach ein Starter mit anderem Namen? Die Antwort lautet nein, und der Unterschied steht in der ersten Zeile — der Wirkzeitpunkt. Alles Weitere folgt daraus.
4:58 Ein Starter bringt Beans und Bedingungen mit, eine Extension bringt Build Steps mit, die Bytecode erzeugen. Deshalb ist die letzte Zeile so wichtig: Fehler tauchen im Buildlauf auf. Und die Fußnote erklärt eine der häufigsten Migrationsüberraschungen: Bedingte Beans im Spring-Stil werden hier ignoriert, weil zum Entscheidungszeitpunkt noch niemand weiß, welche Bedingung später gilt.
5:21 Was gewinnt man dadurch konkret? Beim Start wird nichts mehr geparst, weil das Ergebnis bereits als Bytecode vorliegt. Es werden nur Klassen geladen, die wirklich benutzt werden. Nicht erreichbarer Code lässt sich beim Native Build ganz entfernen. Und Konfigurationsfehler fallen in der Pipeline auf statt im Produktivstart.
5:40 Das ist der eigentliche Gewinn, und er ist größer als die eingesparten Millisekunden: Sie verlagern Risiko von der Laufzeit in den Build — dorthin, wo es billig ist. Die häufigste Irritation steht ganz oben: Eine Konfigurationsoption wird im Container gesetzt und passiert nichts. Sie war eine Build-Time-Property — sie war längst entschieden.
6:01 Der zweite Fall betrifft eigene Erweiterungen: Wer Laufzeitcode in die frühe Phase legt, merkt das spätestens beim Native Build. Dritter Punkt: Der längere Build wird gern als Werkzeugproblem missverstanden, dabei ist er genau die Verlagerung, für die man sich entschieden hat. Und viertens: Auto-Configuration und Extension sind keine Gegensätze — sie tun Ähnliches, nur zu verschiedenen Zeitpunkten.
Konsequenzen für Bibliotheken und Programmiermodell
6:25 Jetzt wird es praktisch relevant. Denn diese architektonische Entscheidung hat sehr konkrete Folgen dafür, welche Ihrer heutigen Abhängigkeiten den Umzug überstehen — und welche nicht. Der wichtigste Satz dieses Moduls steht auf dieser Folie: Quarkus startet keinen Spring Application Context. Spring-Klassen und -Annotationen werden ausschließlich als Metadaten gelesen — als Beschriftung sozusagen. Die Infrastrukturklassen von Spring selbst laufen nie an.
6:53 Das bedeutet: Eine beliebige Spring-Bibliothek können Sie einbinden, sie wird sauber kompilieren, und sie wird nichts tun. Das ist keine Lücke, die noch geschlossen wird, sondern eine Konsequenz des Modells. Wer das akzeptiert, plant realistisch; wer darauf hofft, plant falsch. Vier typische Scheiterfälle. Erweiterungspunkte wie BeanPostProcessor laufen nie an, weil es den Kontext nicht gibt, in dem sie wirken würden.
7:19 Auto-Configuration aus fremden Startern wird nicht ausgewertet. Reflektive Registrierung zur Laufzeit hat im Build-Modell kein Gegenstück. Und das Unangenehmste: Es passiert alles lautlos. Keine Ausnahme, kein Fehler, nur fehlendes Verhalten. Deshalb ist die Inventur der Abhängigkeiten, die wir in Modul siebzehn machen, keine Fleißaufgabe, sondern die eigentliche Risikoanalyse.
7:42 Und das erklärt auch, warum sich diese Fälle nicht durch Nachbessern lösen lassen: Es fehlt kein Feature, es fehlt der Zeitpunkt, zu dem dieses Feature wirken würde. Ein zweiter Punkt, der oft mit der Plattformfrage vermischt wird: imperativ oder reaktiv. Quarkus kann beides, und es zwingt Sie zu nichts. Links steht das vertraute Modell — ein Thread pro Anfrage, lesbarer Kontrollfluss, brauchbare Stacktraces. Rechts wenige Event-Loop-Threads, die nie blockieren dürfen.
8:11 Für die allermeisten Dienste ist links vollkommen richtig. Merken Sie sich vor allem die letzte Zeile: Blockierender Code ist im reaktiven Modell kein Schönheitsfehler, sondern ein Fehler. Wir kommen in Modul dreizehn ausführlich darauf zurück. Der erste Punkt ist der teuerste: Eine Bibliothek wird eingebunden, kompiliert sauber und tut zur Laufzeit nichts.
8:33 Rechnen Sie damit, und prüfen Sie Verhalten statt Übersetzbarkeit. Der zweite ist ein Planungsfehler: Wer den Umstieg als Annotationstausch kalkuliert, hat das Laufzeitmodell übersehen. Der dritte ist eine Modeerscheinung — reaktiv wird gewählt, weil es moderner klingt. Und der vierte rächt sich am spätesten: Testbarkeit und Fehlersuche werden erst nach der Entscheidung betrachtet, obwohl sie den Alltag der nächsten Jahre bestimmen.
Übung
8:59 Genug Theorie. Wenden wir das Gelernte auf unsere Beispielanwendung an — und erstellen die Landkarte, die uns bis zur Migration in Modul siebzehn begleitet. Pfandkreis bringt mit, was in der Praxis üblich ist: Spring Web, Spring Data JPA, Spring Security, Kafka-Anbindung und den Actuator. Ihre Aufgabe ist, jede dieser Abhängigkeiten in genau eine von drei Spalten zu sortieren — direkt übernehmbar, über eine Kompatibilitätsschicht möglich, oder Blocker.
9:27 Und für jeden Blocker halten Sie fest, welche Annahme des Build-Modells er verletzt. Genau diese Begründung ist später Gold wert, wenn jemand fragt, warum ein Dienst nicht migriert wurde. Die fünf Schritte gehen von außen nach innen. Erst die Liste der Abhängigkeiten, dann die Zuordnung zu Extensions oder Kompatibilitätsschichten.
9:47 Danach wird es interessant: Suchen Sie die Stellen, an denen der Application Context selbst benutzt wird — das sind die Stellen, die sich nicht übersetzen lassen, sondern neu gedacht werden müssen. Dasselbe gilt für reflektive Registrierung. Und zu jedem Blocker gehört sofort eine Ersatzidee, und sei sie grob. Eine Liste mit Problemen ohne Lösungsansätze ist keine Landkarte, sondern nur eine Sammlung von Bedenken.
10:11 Der wichtigste Hinweis steht oben: Arbeiten Sie mit dem Abhängigkeitsbaum, nicht mit der Build-Datei. Die interessanten Blocker kommen fast immer transitiv herein, über einen internen Firmenstarter, den niemand mehr im Kopf hat. Zweitens: Dass eine Bibliothek im Build auftaucht, heißt nicht, dass sie unterstützt wird. Drittens wird der Actuator regelmäßig vergessen, weil ihn im Alltag niemand anfasst — der Betrieb schon. Und viertens: Eine Liste aus dem Gedächtnis ist keine Inventur.
10:40 Nutzen Sie die Werkzeuge Ihres Build-Systems.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →