it-swarm.com.de

Frühling - @Transactional - Was passiert im Hintergrund?

Ich möchte wissen, was tatsächlich passiert, wenn Sie eine Methode mit @Transactional Kommentieren. Natürlich weiß ich, dass Spring diese Methode in eine Transaktion einbinden wird.

Aber ich habe folgende Zweifel:

  1. Ich habe gehört, dass Spring eine Proxy-Klasse erstellt ? Kann mir jemand das genauer erklären . Was befindet sich tatsächlich in dieser Proxy-Klasse? Was passiert mit der eigentlichen Klasse? Und wie kann ich die von Spring erstellte Proxy-Klasse sehen?
  2. Ich habe auch in den Frühlingsdokumenten gelesen, dass:

Hinweis: Da dieser Mechanismus auf Proxys basiert, werden nur 'externe' Methodenaufrufe abgefangen, die über den Proxy eingehen . Dies bedeutet, dass 'Selbstaufruf', dh eine Methode innerhalb des Zielobjekts, die eine andere Methode des Zielobjekts aufruft, zur Laufzeit nicht zu einer tatsächlichen Transaktion führt, selbst wenn die aufgerufene Methode mit @Transactional Markiert ist!

Quelle: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Warum werden nur externe Methodenaufrufe unter Transaktion und nicht die Selbstaufrufmethoden ausgeführt?

297
peakit

Das ist ein großes Thema. Das Spring-Referenzdokument widmet ihm mehrere Kapitel. Ich empfehle, die Artikel über aspektorientierte Programmierung und Transaktionen zu lesen, da die Unterstützung deklarativer Transaktionen von Spring AOP als Grundlage verwendet.

Auf einer sehr hohen Ebene erstellt Spring jedoch Proxys für Klassen, die @ Transactional für die Klasse selbst oder für Mitglieder deklarieren. Der Proxy ist zur Laufzeit meist unsichtbar. Es bietet Spring die Möglichkeit, Verhaltensweisen vor, nach oder um Methodenaufrufe in das Objekt einzufügen, für das ein Proxy erstellt wird. Das Transaktionsmanagement ist nur ein Beispiel für das Verhalten, das eingehängt werden kann. Sicherheitsprüfungen sind ein weiteres. Und Sie können auch Ihre eigenen für Dinge wie Protokollierung bereitstellen. Wenn Sie also eine Methode mit @ Transactional annotieren, erstellt Spring dynamisch einen Proxy, der die gleichen Schnittstellen wie die Klasse, die Sie annotieren, implementiert. Und wenn Clients Anrufe in Ihr Objekt tätigen, werden die Anrufe abgefangen und die Verhaltensweisen über den Proxy-Mechanismus eingeleitet.

Transaktionen in EJB funktionieren übrigens ähnlich.

Wie Sie gesehen haben, funktioniert der Proxy-Mechanismus nur, wenn Anrufe von einem externen Objekt eingehen. Wenn Sie einen internen Anruf innerhalb des Objekts tätigen, tätigen Sie tatsächlich einen Anruf über die Referenz " this ", die den Proxy umgeht. Es gibt jedoch Möglichkeiten, dieses Problem zu umgehen. Ich erkläre einen Ansatz in diesem Forumsbeitrag , in dem ich einen BeanFactoryPostProcessor verwende, um eine Instanz des Proxys zur Laufzeit in "selbstreferenzierende" Klassen einzufügen. Ich speichere diesen Verweis auf eine Mitgliedsvariable mit dem Namen " me ". Wenn ich dann interne Anrufe tätigen muss, die eine Änderung des Transaktionsstatus des Threads erfordern, leite ich den Anruf über den Proxy (z. B. " me.someMethod () ". Der Forumsbeitrag erklärt das genauer. Beachten Sie, dass der BeanFactoryPostProcessor Code jetzt etwas anders sein würde, da er bereits im Spring 1.x-Zeitrahmen geschrieben wurde. Aber hoffentlich gibt es Ihnen eine Idee. Ich habe eine aktualisierte Version, die ich wahrscheinlich zur Verfügung stellen könnte.

232
Rob H

Wenn Spring Ihre Bean-Definitionen lädt und so konfiguriert wurde, dass nach @ Transactional-Annotationen gesucht wird, werden diese Proxy-Objekte um Ihr tatsächliches Bean erstellt. Diese Proxy-Objekte sind Instanzen von Klassen, die zur Laufzeit automatisch generiert werden. Das Standardverhalten dieser Proxy-Objekte beim Aufrufen einer Methode besteht nur darin, dieselbe Methode für die "Ziel" -Bean (d. H. Ihre Bean) aufzurufen.

Die Proxys können jedoch auch mit Interceptors versorgt werden. Wenn vorhanden, werden diese Interceptors vom Proxy aufgerufen, bevor die Methode Ihrer Ziel-Bean aufgerufen wird. Für mit @Transactional annotierte Ziel-Beans erstellt Spring einen TransactionInterceptor und übergibt ihn an das generierte Proxy-Objekt. Wenn Sie also die Methode aus dem Client-Code aufrufen, rufen Sie die Methode für das Proxy-Objekt auf, das zuerst den TransactionInterceptor (der eine Transaktion startet) aufruft, der wiederum die Methode für Ihre Ziel-Bean aufruft. Wenn der Aufruf abgeschlossen ist, schreibt der TransactionInterceptor die Transaktion fest oder setzt sie zurück. Es ist für den Client-Code transparent.

Wenn Ihre Bean eine ihrer eigenen Methoden aufruft, geschieht dies nicht über den Proxy. Denken Sie daran, Spring wickelt Ihre Bohne in den Proxy ein, Ihre Bohne hat keine Kenntnis davon. Nur Anrufe von "außerhalb" Ihrer Bean gehen über den Proxy.

Hilft das?

179
skaffman

Als visuelle Person wiege ich gerne mit einem Sequenzdiagramm des Proxy-Musters ein. Wenn Sie nicht wissen, wie man die Pfeile liest, lese ich den ersten wie folgt: Client führt Proxy.method() aus.

  1. Der Client ruft aus seiner Sicht eine Methode auf dem Ziel auf und wird vom Proxy stillschweigend abgefangen
  2. Wenn ein vorheriger Aspekt definiert ist, wird er vom Proxy ausgeführt
  3. Dann wird die eigentliche Methode (Ziel) ausgeführt
  4. After-Return und After-Throwing sind optionale Aspekte, die ausgeführt werden, nachdem die Methode zurückgegeben wurde und/oder wenn die Methode eine Ausnahme auslöst
  5. Danach führt der Proxy den After-Aspekt aus (falls definiert)
  6. Schließlich kehrt der Proxy zum aufrufenden Client zurück

Proxy Pattern Sequence Diagram (Ich durfte das Foto unter der Bedingung posten, dass ich seine Herkunft erwähnte. Autor: Noel Vaes, Website: www.noelvaes.eu)

36
progonkpa

Die einfachste Antwort ist, Für jede Methode, die Sie als @Transactional deklarieren, beginnt die Grenze der Transaktion und endet, wenn die Methode abgeschlossen ist.

Wenn Sie einen JPA-Aufruf verwenden, befinden sich alle Festschreibungen in dieser Transaktionsgrenze. Nehmen wir an, Sie speichern Entity1, Entity2 und Entity3. Beim Speichern von Entity3 tritt nun eine Ausnahme auf, da Enitiy1 und Entity2 in derselben Transaktion eingehen, sodass Entity1 und Entity2 mit Entity3 zurückgesetzt werden.

Transaktion: (entity1.save, entity2.save, entity3.save). Jede Ausnahme führt zum Rollback aller JPA-Transaktionen mit DB. Intern werden JPA-Transaktionen von Spring verwendet.

15