Ich möchte wissen, was tatsächlich passiert, wenn Sie eine Methode mit Anmerkungen versehen @Transactional
. Natürlich weiß ich, dass Spring diese Methode in eine Transaktion einbinden wird.
Aber ich habe folgende Zweifel:
- Ich habe gehört, dass Spring eine Proxy-Klasse erstellt ? Kann dies jemand in mehr erklärt Tiefe . Was befindet sich tatsächlich in dieser Proxy-Klasse? Was passiert mit der eigentlichen Klasse? Und wie kann ich Spring's erstellte Proxy-Klasse sehen?
- Ich habe auch in Spring Docs 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 markiert ist
@Transactional
!
Quelle: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Warum werden nur externe Methodenaufrufe unter Transaktion und nicht die Selbstaufrufmethoden angezeigt?
Antworten:
Das ist ein großes Thema. Das Spring-Referenzdokument widmet ihm mehrere Kapitel. Ich empfehle, diejenigen zu aspektorientierter Programmierung und Transaktionen zu lesen , da die deklarative Transaktionsunterstützung 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 zu proxyierende Objekt einzufügen. Das Transaktionsmanagement ist nur ein Beispiel für das Verhalten, das eingebunden werden kann. Sicherheitsüberprüfungen sind ein weiteres. Und Sie können auch Ihre eigenen für Dinge wie die Protokollierung bereitstellen. Wenn Sie also eine Methode mit @Transactional mit Anmerkungen versehen , erstellt Spring dynamisch einen Proxy, der dieselben Schnittstellen implementiert wie die Klasse, die Sie mit Anmerkungen versehen. Und wenn Clients Anrufe in Ihr Objekt tätigen, werden die Anrufe abgefangen und das Verhalten über den Proxy-Mechanismus injiziert.
Transaktionen in EJB funktionieren übrigens ähnlich.
Wie Sie bereits bemerkt 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 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 namens " me ". Wenn ich dann interne Aufrufe tätigen muss, die eine Änderung des Transaktionsstatus des Threads erfordern, leite ich den Aufruf über den Proxy (z. B. " me.someMethod ()".".) Der Forumsbeitrag erklärt dies ausführlicher. Beachten Sie, dass der BeanFactoryPostProcessor- Code jetzt etwas anders wäre, da er im Zeitrahmen von Spring 1.x geschrieben wurde. Aber hoffentlich gibt er Ihnen eine Idee. Ich habe eine aktualisierte Version, die Ich könnte wahrscheinlich zur Verfügung stellen.
quelle
BeanFactoryPostProcessor
. Es gibt jedoch eine (meiner Meinung nach) sehr ähnliche Methode, die in dieser Antwort beschrieben wird: stackoverflow.com/a/11277899/3667003 ... und weitere Lösungen im gesamten Thread.Wenn Spring Ihre Bean-Definitionen lädt und so konfiguriert wurde, dass nach
@Transactional
Anmerkungen gesucht wird, werden diese Proxy-Objekte um Ihre eigentliche 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 darin, dieselbe Methode nur für die "Ziel" -Bohne (dh Ihre Bean) aufzurufen.Die Proxys können jedoch auch mit Interceptors ausgestattet werden. Wenn diese Interceptors vorhanden sind, werden sie vom Proxy aufgerufen, bevor die Methode Ihrer Ziel-Bean aufgerufen wird. Für mit Bots versehene Ziel-Beans erstellt
@Transactional
Spring einTransactionInterceptor
und übergibt es an das generierte Proxy-Objekt. Wenn Sie also die Methode aus dem Clientcode aufrufen, rufen Sie die Methode für das Proxy-Objekt auf, das zuerst die Methode aufruftTransactionInterceptor
(die eine Transaktion startet), die wiederum die Methode für Ihre Ziel-Bean aufruft. Wenn der Aufruf abgeschlossen ist, wirdTransactionInterceptor
die Transaktion festgeschrieben / zurückgesetzt. Es ist transparent für den Client-Code.Was die "externe Methode" betrifft, wenn Ihre Bean eine ihrer eigenen Methoden aufruft, wird dies nicht über den Proxy geschehen. Denken Sie daran, Spring wickelt Ihre Bohne in den Proxy, Ihre Bohne hat keine Kenntnis davon. Nur Anrufe von "außerhalb" Ihrer Bean gehen über den Proxy.
Hilft das?
quelle
Als visuelle Person mag ich es, mit einem Sequenzdiagramm des Proxy-Musters abzuwägen. Wenn Sie nicht wissen, wie man die Pfeile liest, lese ich den ersten wie
Client
folgt : führt ausProxy.method()
.(Ich durfte das Foto unter der Bedingung veröffentlichen, dass ich seine Herkunft erwähnte. Autor: Noel Vaes, Website: www.noelvaes.eu)
quelle
Die einfachste Antwort lautet:
Bei jeder Methode, die Sie deklarieren
@Transactional
, beginnt und endet die Grenze der Transaktion, wenn die Methode abgeschlossen ist.Wenn Sie einen JPA-Aufruf verwenden, befinden sich alle Commits in dieser Transaktionsgrenze .
Nehmen wir an, Sie speichern Entity1, Entity2 und Entity3. Jetzt , während entity3 Speicher eine Ausnahme auftreten , dann als enitiy1 und entity2 in derselben Transaktion kommt so entity1 und entity2 sein Rollback mit entity3.
Transaktion:
Jede Ausnahme führt zum Rollback aller JPA-Transaktionen mit DB. Intern werden JPA-Transaktionen von Spring verwendet.
quelle
Es mag spät sein, aber ich bin auf etwas gestoßen, das Ihre Bedenken in Bezug auf den Proxy erklärt (nur "externe" Methodenaufrufe, die über den Proxy eingehen, werden abgefangen).
Zum Beispiel haben Sie eine Klasse, die so aussieht
und Sie haben einen Aspekt, der so aussieht:
Wenn Sie es so ausführen:
}}
Ergebnisse des Aufrufs von kickOff über dem oben angegebenen Code.
aber wenn Sie Ihren Code ändern
Sie sehen, die Methode ruft intern eine andere Methode auf, damit sie nicht abgefangen wird und die Ausgabe folgendermaßen aussehen würde:
Sie können dies umgehen, indem Sie dies tun
Code-Schnipsel aus: https://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy/
quelle
Alle vorhandenen Antworten sind richtig, aber ich glaube, ich kann nicht nur dieses komplexe Thema geben.
Eine umfassende, praktische Erklärung finden Sie in diesem ausführlichen Spring @Transactional- Handbuch, in dem versucht wird, das Transaktionsmanagement in ~ 4000 einfachen Worten mit vielen Codebeispielen zu behandeln.
quelle