[JAVA] L'essence de l'humeur d'AspectJ - pourquoi votre `@ Transactional` est ignoré

La bibliothèque de programmation orientée aspect (AOP) AspectJ est utile pour intercepter les appels de méthode et insérer des traitements arbitraires dans un environnement JVM et des cadres. Il est souvent utilisé comme une mise en œuvre de travail.

Par exemple, il y a pas mal de cas où AspectJ (proxy CGLIB [^ cglib]) est utilisé pour réaliser des transactions avec l'annotation @ Transactional même dans le domaine où Spring Framework est utilisé [^ aop-impl].

[^ cglib]: Bien qu'il soit appelé "proxy CGLIB" dans la documentation officielle de Spring, il fait référence à AspectJ (GCLIB est le nom de la bibliothèque de génération de code utilisée en interne par AspectJ).

[^ aop-impl]: Spring utilise par défaut [java.lang.reflect.Proxy](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/ lang / reflect / Proxy.html) ne peut être AOP que pour l'interface, et il existe de nombreux cas où AspectJ est utilisé en raison de ses restrictions.

Le AspectJ largement utilisé est en fait utile, mais dans les projets où il fonctionne ** "My @ Transactional est ignoré après des heures d'essais et d'erreurs ... pourquoi ...!" J'ai entendu le triste cri de ** à plusieurs reprises sur divers sites.

Au printemps, si vous lisez les chapitres du document officiel Chapitre 6. Programmation orientée aspect avec Spring, l'arrière-plan sera également Je connais tous les principes de fonctionnement et les options possibles, mais si vous rencontrez des problèmes avec ** "Je veux déplacer mon AOP maintenant !!" **, voir ci-dessous:

Pourquoi votre AOP est ignoré

1) Méthodes qui ne peuvent pas être remplacées

Le symptôme est que l'AOP ne fonctionne pas si les conditions suivantes sont remplies:

Rendons possible le dépassement.

2) Ne fait pas référence à une instance du wrapper généré par AspectJ

Modèles communs:

Pourquoi cela se produit-le principe de fonctionnement d'AspectJ

AspectJ atteint l'AOP en suivant les étapes suivantes:

  1. Générez dynamiquement une classe qui hérite de la classe de l'instance d'origine et remplacez toutes les méthodes
  2. Créez une instance en créant une nouvelle classe ci-dessus (une instance qui encapsule l'instance d'origine)
  3. La méthode wrapper exécute le processus AOP, puis appelle la méthode de l'instance d'origine [^ override]

[^ override]: c'est pourquoi la méthode cible doit pouvoir remplacer

Le point important est que ** AspectJ crée une autre classe et son instance (wrapper) qui hérite de la classe que vous avez écrite **.

Donc, si vous appelez une méthode sur une instance du wrapper, AOP fonctionnera, mais si vous appelez une méthode sur une instance de la classe d'origine, AOP ne fonctionnera pas du tout.

La solution de contournement consiste à obtenir l'instance à partir de laquelle la méthode est appelée à partir du framework (qui prend en charge AspectJ).

Par exemple, la solution de contournement spécifique pour le modèle «2)» mentionné ci-dessus est la suivante:

Résumé

Comme ceux qui ont lu toutes les explications jusqu'à présent le savent déjà, cela revient au principe simple que ** AspectJ appelle la méthode sans passer par le wrapper généré par héritage / remplacement **.

Dans les articles dans le monde, il y a des moments où il y a une explication et une énumération que "AspectJ ne fonctionne pas bien dans ce cas, dans un tel cas, dans un tel cas", et quand j'ai vu cela, "Quelle bibliothèque mystérieuse compliquée. J'ai vu des gens désespérés à plusieurs reprises, mais en substance, c'est juste ce problème (je vais probablement l'expliquer à l'avenir, donc le texte Je l'ai résumé en).

Même si les exemples spécifiques évoqués jusqu'à présent ne s'appliquent pas directement, vous pouvez probablement en trouver la cause en pensant au principe de fonctionnement d'AspectJ évoqué dans cet article.

Bonus: le rollbackFor de Spring @ Transactional

By default, a transaction will be rolling back on RuntimeException and Error but not on checked exceptions (business exceptions). See DefaultTransactionAttribute.rollbackOn(Throwable) for a detailed explanation.

Javadoc officiel

Bien que sans rapport avec le sujet de cet article, «@ Transactional» de Spring ne supprime pas les exceptions de vérification (par exemple, «IOException») par défaut, donc «la transaction a été validée même si une exception s'est produite. Il y a souvent une histoire qui dit "Ta!" Par conséquent, il existe un certain savoir-faire pour lequel il est moins déroutant de spécifier explicitement rollbackFor.

Recommended Posts

L'essence de l'humeur d'AspectJ - pourquoi votre `@ Transactional` est ignoré
'% 02d' Quel est le% de% 2?
Votre utilisation de JobScheduler est incorrecte
Qu'est-ce qu'un test? ・ À propos de l'importance d'un test
Vérifiez le caractère unique de votre adresse e-mail
Quelle est la structure des données d'ActionText?
Qu'est-ce que JSP? ~ Connaissons les bases de JSP !! ~
Le trait de soulignement ActiveSupport n'est pas la conversion inverse de camelize
L'ordre des modificateurs de méthode Java est fixe
Améliorer les performances de l'environnement de développement Docker
Le nom officiel de Spring MVC est Spring Web MVC