Start / Seminare / Jakarta EE modernisieren
Modul
CDI, EJB und Transaktionen modernisieren
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.
CDI, EJB und Transaktionen modernisieren
0:00 Dieses Modul behandelt den Kern jeder Enterprise-Java-Anwendung: die Beans und die Transaktionen. Und es beginnt mit einer Einsicht, die den ganzen Umbau bestimmt: Eine Stateless Session Bean ist kein Bauteil, sondern ein Bündel von Zusagen. Sie erledigt Injektion, Transaktion, Nebenläufigkeitsschutz, Zeitsteuerung und Sicherheit gleichzeitig — mit einer einzigen Annotation.
0:23 In der neuen Laufzeit gibt es dafür keine einzelne Entsprechung, sondern mehrere Bausteine. Wer das weiß, migriert sauber. Wer nur die Annotation tauscht, verliert unterwegs Zusagen, ohne es zu merken.
CDI, EJB und Transaktionen modernisieren
0:36 Wir arbeiten weiter am Piloten. Nach der Schnittstelle kommt jetzt die Schicht dahinter — die Beans, die die Fachlogik tragen, und die Transaktionsgrenzen, die dafür sorgen, dass am Ende alles oder nichts geschrieben wird. Das ist der Teil, bei dem eine Migration am ehesten still danebengeht, weil das Ergebnis funktioniert und trotzdem anders ist.
Dependency Injection
0:56 Beginnen wir mit dem Vertrauten: CDI bleibt CDI. Nur der Zeitpunkt ändert sich, zu dem entschieden wird, welche Bean wohin gehört. Diese Verschiebung in den Build hat Folgen, die bei gewachsenem Code regelmäßig auffallen — meistens dort, wo Reflection im Spiel ist. Die Idee von CDI lässt sich mit einem Restaurant erklären: Sie kochen nicht selbst und kaufen auch nicht ein — Sie bestellen, und das fertige Gericht kommt an den Tisch.
1:23 Genauso sagt eine Komponente nur, was sie braucht, und der Container liefert das passende Objekt. Quarkus setzt das über ArC um. Die Spezifikation CDI Lite ist vollständig umgesetzt, aus CDI Full nur ausgewählte Teile. Und der wichtige Unterschied: Die Beans werden beim Build ermittelt, nicht beim Start. Das macht den Start schnell — und die Dynamik danach begrenzt.
1:46 Die Logik dieser Tabelle liegt in der mittleren Spalte, dem Zeitpunkt der Erzeugung. ApplicationScoped erzeugt beim ersten Methodenaufruf, über einen Proxy — Singleton dagegen sofort beim Injizieren, ohne Proxy. Das klingt akademisch, hat aber praktische Folgen: Wenn eine Bean beim Erzeugen eine Datenbankverbindung aufbaut, entscheidet der Scope darüber, ob das beim Start oder beim ersten Aufruf passiert.
2:08 Und die Fußzeile ist für Migrationen wichtig: SessionScoped ist nicht selbstverständlich da, sondern hängt an einer bestimmten Erweiterung. Anwendungen mit Sitzungszustand brauchen hier eine bewusste Entscheidung. Der erste Punkt ist der, der bei Bestandscode am häufigsten zuschlägt: Unbenutzte Beans werden entfernt. Der Build sieht keinen Injektionspunkt und schließt daraus, dass die Bean nicht gebraucht wird — was stimmt, solange niemand sie per Reflection sucht.
2:36 Bei gewachsenen Anwendungen tun das erstaunlich viele Stellen. Punkt zwei betrifft fremde Bibliotheken: Portable Extensions gibt es nicht, und Bibliotheken, die darauf bauen, wirken einfach nicht. Ohne Fehlermeldung. Die Abhilfe steht in Punkt vier: Beans, die erhalten bleiben müssen, werden ausdrücklich markiert. Worauf es hier ankommt, ist das Muster, nicht die Zeilen. Ein Producer stellt ein Objekt bereit, das nicht selbst eine Bean sein kann — hier eine Zeitdauer aus der Konfiguration.
3:05 Der Qualifier gibt ihm einen Namen, damit an der Injektionsstelle klar ist, welche Dauer gemeint ist. Dieses Muster ersetzt in Migrationen sehr häufig die alten JNDI-Lookups: Statt zur Laufzeit nach einem Namen zu suchen, wird der Wert zur Bauzeit bereitgestellt und typsicher injiziert. Und die Fußzeile ist der Rettungsanker für den Reflection-Fall aus der vorigen Folie.
3:27 Punkt drei ist der, den man am häufigsten übersieht: Ein Interceptor greift nicht, weil der Aufruf innerhalb derselben Klasse erfolgt. Das gilt für Transaktionen genauso wie für Sicherheitsprüfungen — der Container kann nur eingreifen, wenn der Aufruf durch den Proxy geht. Bei einem Selbstaufruf tut er das nicht. Das ist übrigens kein Quarkus-Thema, es galt im Application Server ebenso; aber beim Umbau werden Methoden verschoben, und dabei entstehen neue Selbstaufrufe.
3:55 Punkt vier ist der klassische Hintergrundlauf-Fehler: Der RequestScope fehlt, weil kein HTTP-Aufruf dahintersteht.
EJB-Funktionen ersetzen
4:02 Jetzt zum Kern des Moduls. Was eine Session Bean gleichzeitig war, wird zu mehreren Bausteinen — und jeder muss einzeln nachgebildet werden. Wir gehen die Zusagen einzeln durch: Injektion, Transaktion, Zeitsteuerung, Sicherheit — und den Nebenläufigkeitsschutz, für den es keine fertige Antwort gibt. Eine Stateless Session Bean bündelte fünf Dinge in einer Annotation: Injektion, Transaktion, Nebenläufigkeitsschutz, Zeitsteuerung und Sicherheit.
4:30 Das war bequem, und es war intransparent — kaum jemand hätte spontan alle fünf aufzählen können. In der neuen Laufzeit übernimmt CDI die Injektion, die Transaktionsannotation die Grenze, die Scheduler-Erweiterung die Zeitsteuerung und die Security-Annotation die Rollen. Das ist mehr Schreibarbeit, aber es ist sichtbar. Und Sichtbarkeit ist bei einer Migration ein Vorteil: Was man aufschreiben muss, kann man nicht versehentlich vergessen.
4:57 Diese vier Schichten sind Ihre Prüfliste für jede migrierte Bean. Gehen Sie sie einzeln durch und fragen Sie: Ist diese Zusage abgebildet? Bei den ersten drei ist die Antwort meist schnell gefunden. Die vierte — Sicherheit — wird gern vertagt, weil Modul zwölf sie behandelt. Genau dadurch fällt sie hinten runter: Die Bean funktioniert ja, sie prüft nur keine Rolle mehr.
5:18 Und was in diesem Diagramm bewusst fehlt, ist der Nebenläufigkeitsschutz einer Singleton Bean — für den gibt es keine automatische Entsprechung. Darüber sprechen wir gleich. Worauf es hier ankommt, ist das Zusammenspiel dreier Annotationen an einer Methode: eine Bean, eine Zeitsteuerung, eine Transaktionsgrenze. Beachten Sie, dass der Zeitplan aus der Konfiguration kommt und nicht im Code steht — das ist für Migrationen wichtig, weil sich Zeitpläne je Umgebung unterscheiden; im Testsystem soll der nächtliche Lauf vielleicht gar nicht starten.
5:50 Und die Fußzeile enthält die entscheidende Einschränkung: Sobald mehrere Instanzen laufen, startet dieser Lauf in jeder Instanz. Für den geclusterten Betrieb ist die Quartz-Erweiterung vorgesehen. Vier Zusagen, die beim Tausch der Annotationen verschwinden können. Der erste Punkt ist der subtilste: Der Nebenläufigkeitsschutz einer Singleton Bean hat keine automatische Entsprechung.
6:13 Im Application Server hat der Container die Aufrufe serialisiert; jetzt laufen sie parallel. Wenn die Bean Zustand hält, haben Sie ein neues Problem, das im Test selten auftritt. Punkt zwei ist der, der Zählwerk direkt trifft — der Timer in jeder Instanz. Und Punkt vier verbindet zu Modul zwölf: Der Sicherheitskontext kommt nicht mehr vom Server, sondern aus dem Token.
6:36 Punkt zwei ist der konkrete Schaden, über den wir gerade gesprochen haben: Der Ableselauf startet in drei Instanzen und legt jeden Auftrag dreifach an. Das ist kein hypothetisches Szenario — es ist der Standardfall, wenn man nichts unternimmt, und es fällt oft erst beim Monatsabgleich auf. Punkt drei ist der Rest aus der alten Welt: JNDI-Lookups bleiben im Code stehen. Sie kompilieren einwandfrei, denn es sind Zeichenketten, und sie scheitern erst zur Laufzeit.
7:02 Deshalb stand die Textsuche danach schon in Modul zwei auf der Liste.
Transaktionen
7:07 Bleiben die Transaktionen. Dieselbe Annotation, dieselben Typen — aber eine andere Reichweite. Und genau diese Reichweite ist entscheidend. Denn was der Application Server über mehrere Ressourcen hinweg geklammert hat, klammert die neue Laufzeit nicht mehr. Transaktionsgrenzen werden weiterhin mit der Annotation Transactional gesetzt; die Narayana-Erweiterung stellt Jakarta Transactions bereit.
7:32 Die bekannten Typen gelten unverändert: REQUIRED, REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED und NEVER. Das ist die gute Nachricht — Sie müssen kein neues Modell lernen. Die weniger gute steht auf der übernächsten Folie: Was der Application Server über mehrere Ressourcen hinweg geklammert hat, klammert die neue Laufzeit nicht.
7:52 Prüfen Sie also nicht nur, ob die Annotation da ist, sondern was sie tatsächlich umspannt. Die Logik dieser Tabelle steht in der dritten Spalte, dem typischen Einsatz. Interessant ist die zweite Zeile: REQUIRES_NEW für den Protokolleintrag, der auch dann bestehen bleiben soll, wenn die eigentliche Transaktion zurückgerollt wird.
8:13 Dieses Muster findet sich in fast jeder Bestandsanwendung — und es ist genau die Stelle, an der eine gedankenlose Migration Protokolleinträge verliert. Und beachten Sie die Fußzeile: Das Standard-Zeitlimit liegt bei sechzig Sekunden. Für einen HTTP-Aufruf reichlich, für einen Massenlauf über zehntausend Zähler viel zu wenig.
8:32 Das muss ausdrücklich gesetzt werden. Der erste Punkt ist die harte Grenze: Verteilte Transaktionen über mehrere Ressourcen werden nicht unterstützt. Wenn Ihre Anwendung heute Datenbank und Nachrichtenwarteschlange gemeinsam klammert, brauchen Sie einen anderen Entwurf — meist Idempotenz und Wiederholung statt Klammer. Genau das behandeln wir in Modul elf. Punkt zwei ist die konzeptionelle Verschiebung: Die Grenze endet an der Laufzeit, nicht am Geschäftsprozess.
8:59 Und Punkt vier ist praktisch nützlich: Ein Rollback lässt sich über den injizierten TransactionManager erzwingen, ohne eine Ausnahme werfen zu müssen. Der rote Faden: aufschreiben, bewusst neu ziehen, prüfen, beweisen. Schritt zwei ist die eigentliche Botschaft — die Grenze bewusst neu ziehen, statt sie zu übernehmen. Denn übernommen wird sonst eine Grenze, die auf Zusagen beruhte, die es nicht mehr gibt.
9:23 Und Schritt vier ist der, ohne den alles Vorherige wertlos wäre: einen Fehlerfall auslösen und das Rollback nachweisen. Wie die Fußzeile sagt — ohne diesen Schritt ist die Grenze eine Behauptung. Bauen Sie diesen Test ein, er kostet zwanzig Minuten und wandert direkt in die Testsammlung aus Modul dreizehn. Punkt eins ist der häufigste Entwurfsfehler: Eine Transaktion umspannt einen HTTP-Aufruf. Solange das Fremdsystem schnell antwortet, fällt es nicht auf.
9:51 Wenn es hängt, hängt Ihre Datenbanktransaktion mit — und bei sechzig Sekunden Zeitlimit reißt sie ab. Punkt zwei ist der Verlust, über den wir gesprochen haben: Der Protokolleintrag verschwindet mit dem Rollback. Und Punkt vier ist die Wiederholung des Interceptor-Themas in seiner gefährlichsten Form: Ein Selbstaufruf umgeht die Annotation, und es entsteht gar keine Transaktion.
10:14 Der Code läuft, die Daten werden geschrieben — nur eben ohne Klammer.
Übung
10:19 In der Übung bauen Sie die Beans und den nächtlichen Lauf von Zählwerk um. Und Sie weisen nach, dass die Klammer noch hält. Der Nachweis ist dabei wichtiger als der Umbau: Ein Rollback, das man nur annimmt, ist kein Rollback. Sie ersetzen eine Stateless Session Bean und einen Timer durch CDI-Beans mit Scheduler. Das Lernziel ist, die Zusagen einer Enterprise Bean einzeln zu benennen und nachzubilden — also genau die Zerlegung, über die wir gesprochen haben.
10:45 Erfolgreich sind Sie, wenn der nächtliche Ableselauf nur einmal startet und ein ausgelöster Fehler beim Abschluss nachweislich zum vollständigen Rollback führt. Die bisherigen Zusagen stehen im Inventar aus Modul zwei; jede davon braucht hier eine Entsprechung — oder eine ausdrückliche Streichung mit Begründung. Fünf Schritte, und Schritt eins ist der wichtigste: die Zusagen auflisten, bevor Sie Code anfassen.
11:08 Injektion, Transaktion, Timer, Rollen — vier Zeilen, fünf Minuten Arbeit, und Sie haben eine Prüfliste, gegen die Sie am Ende abgleichen können. Schritt vier verlangt eine Entscheidung, keine Konfiguration: Wie verhindern Sie den Mehrfachstart im Cluster, und warum so? Und Schritt fünf ist der Beweis. Der Test aus diesem Schritt wandert direkt in die Testsammlung aus Modul dreizehn — Sie schreiben ihn also nur einmal.
11:34 Punkt eins ist der, den ich in dieser Übung am häufigsten sehe: Die Rollenprüfung entfällt, weil sie bisher der Server übernahm. Die Bean funktioniert, die Tests sind grün, und die Methode ist offen für jeden. Punkt zwei ist der Mehrfachstart, über den wir mehrfach gesprochen haben — er passiert, wenn man nichts tut. Punkt drei ist die Bequemlichkeit: Das Rollback wird angenommen statt geprüft.
11:57 Und Punkt vier ist die Zeitbombe: Die alte JNDI-Suche bleibt im Code und stirbt erst in der Abnahme — im besten Fall.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →