web-dev-qa-db-fra.com

Spring - @Transactional - Que se passe-t-il en arrière-plan?

Je veux savoir ce qui se passe réellement lorsque vous annotez une méthode avec @Transactional? Bien sûr, je sais que Spring intégrera cette méthode dans une transaction.

Mais j'ai les doutes suivants:

  1. J'ai entendu dire que Spring crée une classe proxy ? Quelqu'un peut-il expliquer cela en plus profondeur . Qu'est-ce qui réside réellement dans cette classe de proxy? Qu'advient-il de la classe actuelle? Et comment puis-je voir la classe avec proxy créée par Spring
  2. J'ai aussi lu dans Spring docs que:

Remarque: Étant donné que ce mécanisme repose sur des mandataires, , seuls les appels de méthode 'externes' entrant par le biais du proxy seront interceptés . Cela signifie que 'auto-invocation', c'est-à-dire qu'une méthode de l'objet cible appelant une autre méthode de l'objet cible, ne conduira pas à une transaction réelle au moment de l'exécution, même si la méthode invoquée est marquée avec @Transactional!

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

Pourquoi seuls les appels de méthode externes seront sous transaction et non les méthodes d'auto-invocation?

297
peakit

C'est un gros sujet. Le document de référence Spring y consacre plusieurs chapitres. Je recommande de lire celles sur Programmation par aspect et Transactions , car le support de transaction déclaratif de Spring utilise AOP à sa base.

Mais à un niveau très élevé, Spring crée des mandataires pour les classes qui déclarent @ Transactional sur la classe elle-même ou sur des membres. Le proxy est principalement invisible au moment de l'exécution. Cela permet à Spring d’injecter des comportements avant, après ou autour des appels de méthode à l’objet traité par proxy. La gestion des transactions n'est qu'un exemple des comportements pouvant être associés. Les contrôles de sécurité en sont un autre. Et vous pouvez également fournir les vôtres, par exemple pour la journalisation. Ainsi, lorsque vous annotez une méthode avec @ Transactional , Spring crée dynamiquement un proxy qui implémente la même interface que la classe que vous annotez. Et lorsque les clients passent des appels dans votre objet, les appels sont interceptés et les comportements injectés via le mécanisme de proxy.

Les transactions dans EJB fonctionnent de la même manière, d'ailleurs.

Comme vous l'avez observé, le mécanisme de proxy ne fonctionne que lorsque les appels arrivent d'un objet externe. Lorsque vous effectuez un appel interne dans l'objet, vous effectuez un appel via la référence " this ", qui contourne le proxy. Il existe toutefois des moyens de contourner ce problème. J'explique une approche dans cet article de forum dans lequel j'utilise un BeanFactoryPostProcessor pour injecter une instance du proxy dans "self- référençant "les classes au moment de l'exécution. Je sauvegarde cette référence dans une variable membre appelée " me ". Ensuite, si je dois effectuer des appels internes nécessitant une modification de l'état de la transaction du thread, je dirige l'appel via le proxy (par exemple, " me.someMethod () ".) Le message du forum explique plus en détail. Notez que le code BeanFactoryPostProcessor serait un peu différent maintenant, car il a été écrit à l’époque Spring 1.x. Mais j'espère que cela vous donne une idée. J'ai une version mise à jour que je pourrais probablement mettre à disposition.

232
Rob H

Lorsque Spring charge vos définitions de bean et qu'il a été configuré pour rechercher des annotations @Transactional, il crée ces objets proxy autour de votre bean réel. Ces objets proxy sont des instances de classes générées automatiquement au moment de l'exécution. Le comportement par défaut de ces objets proxy lorsqu'une méthode est appelée consiste simplement à appeler la même méthode sur le bean "cible" (c'est-à-dire votre bean).

Cependant, les mandataires peuvent également être fournis avec des intercepteurs et, lorsqu'ils sont présents, ces intercepteurs seront appelés par le proxy avant qu'il n'invoque la méthode de votre bean cible. Pour les beans cibles annotés avec @Transactional, Spring créera un TransactionInterceptor et le transmettra à l'objet proxy généré. Ainsi, lorsque vous appelez la méthode à partir du code client, vous appelez la méthode sur l'objet proxy, qui appelle d'abord le TransactionInterceptor (qui commence une transaction), qui à son tour appelle la méthode sur votre bean cible. Une fois l'appel terminé, TransactionInterceptor valide/annule la transaction. C'est transparent pour le code client.

En ce qui concerne la "méthode externe", si votre bean invoque l'une de ses propres méthodes, il ne le fera pas via le proxy. Rappelez-vous, Spring enveloppe votre haricot dans le proxy, votre haricot n'en a aucune connaissance. Seuls les appels de "l'extérieur" de votre haricot passent par le proxy.

Est ce que ça aide?

179
skaffman

En tant que personne visuelle, j'aime peser avec un diagramme de séquence du motif de substitution. Si vous ne savez pas lire les flèches, je lis le premier comme ceci: Client exécute Proxy.method().

  1. Le client appelle une méthode sur la cible de son point de vue et est intercepté en silence par le proxy.
  2. Si un aspect avant est défini, le proxy l'exécutera
  3. Ensuite, la méthode actuelle (cible) est exécutée
  4. Après le retour et après le lancement sont des aspects facultatifs qui sont exécutés après le retour de la méthode et/ou si la méthode lève une exception
  5. Après cela, le proxy exécute l'aspect après (si défini)
  6. Enfin, le proxy retourne au client appelant

Proxy Pattern Sequence Diagram (J'ai été autorisé à poster la photo à condition de mentionner ses origines. Auteur: Noel Vaes, site web: www.noelvaes.eu)

36
progonkpa

La réponse la plus simple est, quelle que soit la méthode que vous déclarez @Transactional, la limite de la transaction commence et la limite se termine à la fin de la méthode.

Si vous utilisez un appel JPA, tous les validations sont effectuées dans cette limite de transaction. Disons que vous enregistrez entity1, entity2 et entity3. Désormais, lors de la sauvegarde de entity3, une exception survient, car enitiy1 et entity2 entrent dans la même transaction; ainsi, entity1 et entity2 seront restaurés avec entity3.

Transaction: (entity1.save, entity2.save, entity3.save). Toute exception entraînera l'annulation de toutes les transactions JPA avec DB. En interne, les transactions JPA sont utilisées par Spring.

15
RoshanKumar Mutha