Cette fois, j'écrirai sur l'ordre d'exécution de Spring AOP. Nous allons vous montrer comment prioriser les conseils dans le même Aspect et comment prioriser les mêmes Conseils entre différents Aspects!
Pour une brève explication de Spring AOP lui-même, voir Qiita (First Spring AOP), donc si vous voulez en savoir plus sur AOP, veuillez vous y référer. Ce sera fluide si vous le lisez après l'avoir reçu.
Il existe une priorité dans l'ordre dans lequel l'avis est exécuté. [^ 1]
@Around
@Before
@After
@AfterReturning
@AfterThrowing
[^ 1]: Puisqu'il s'agira d'un document de la série Spring-core 5.3, il est légèrement différent de la version (5.2.6) utilisée cette fois, mais 5.2.6 ne spécifie pas la priorité de Advice Comme il n'y avait pas de divergence avec les résultats expérimentaux décrits plus loin, je me suis référé à la série 5.3 uniquement pour cette partie. https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/core.html#aop-ataspectj-advice-ordering
Autrement dit, si «@ Around» et «@ Before» existent dans le même point de jointure, le processus de pré-exécution défini dans «@ Around» est exécuté en premier.
Alors, que se passe-t-il s'il y a @ Before
, @ After
, @ AfterReturning
et @ Around
, qui sont traités avant et après l'exécution du point de jointure, au même point de jointure dans le même aspect?
J'ai expérimenté un code HelloWorld simple composé des 3 classes suivantes.
Une brève introduction et un code source sont fournis pour chacun.
① Classe principale
Une classe qui exécute simplement Spring. Aucun traitement spécial n'est effectué.
Puisque @ EnableAspectJAutoProxy
est une annotation requise pour activer AOP, elle est ajoutée à toutes les classes cette fois.
AopSampleApplication.java
package com.example.practiceaop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@EnableAspectJAutoProxy
@SpringBootApplication
public class AopSampleApplication {
public static void main(String[] args) {
SpringApplication.run(AopSampleApplication.class, args);
}
}
② Classe de contrôleur Appuyez sur http: // localhost: 8080 / pour afficher [Hello World!] Sur la console. J'ai beaucoup utilisé le code de l'article Qiita que j'ai écrit avant (je suis content de l'avoir fait!).
AopSampleController.java
package com.example.practiceaop;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@EnableAspectJAutoProxy
public class AopSampleController {
@RequestMapping("/")
@ResponseBody
public void helloWorld() {
System.out.println("【Hello World!】");
}
}
③ Classe qui définit l'aspect Le point de jonction est helloWorld () de AopSampleController, qui est commun à tous les conseils.
Aspect1.java
package com.example.practiceaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Aspect
@Component
@EnableAspectJAutoProxy
public class Aspect1 {
@Before("execution(* com.example.practiceaop.AopSampleController.helloWorld(..))")
public void before1(JoinPoint jp) {
System.out.println("Before1");
}
@After("execution(* com.example.practiceaop.AopSampleController.helloWorld(..))")
public void after1(JoinPoint jp) {
System.out.println("After1");
}
@AfterReturning("execution(* com.example.practiceaop.AopSampleController.helloWorld(..))")
public void afterReturning1(JoinPoint jp) {
System.out.println("AfterReturning1");
}
@Around("execution(* com.example.practiceaop.AopSampleController.helloWorld(..))")
public Object around1(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Avant d'exécuter la méthode Around1");
try {
Object result = pjp.proceed();
System.out.println("Après avoir exécuté la méthode Around1");
return result;
} catch (Throwable e) {
throw e;
}
}
}
Le résultat de l'exécution est le suivant.
Avant d'exécuter la méthode Around1
Before1
【Hello World!】
AfterReturning1
After1
Après avoir exécuté la méthode Around1
Pourquoi l'ordre d'exécution se trouve-t-il en bas après le point de jointure alors que la priorité Autour devrait être la première? [Documentation officielle](https://docs.spring.io/spring-framework/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-ataspectj-advice-ordering] pour vous aider à comprendre ) Est traduit en japonais par Traduction approfondie (l'apparence est ajustée un peu manuellement).
Spring AOP suit les mêmes règles de priorité qu'AspectJ pour déterminer l'ordre dans lequel le conseil est exécuté. Celui qui a la priorité la plus élevée est exécuté en premier (c'est-à-dire que s'il y a deux conseils Before, celui qui a la priorité la plus élevée est exécuté en premier). À la sortie du point de jointure, l'avis de priorité la plus élevée est exécuté en dernier (donc si deux conseils After sont donnés, la priorité la plus élevée est exécutée en second).
«À la sortie du point de jonction, le conseil de haute priorité est exécuté en dernier», ce qui prête à confusion. En d'autres termes, avant que la méthode de point de jointure ne soit exécutée, la priorité est égale à l'ordre d'exécution, mais après l'exécution de la méthode de point de jointure, celle avec la priorité la plus faible est exécutée en premier.
Par exemple, supposons que vous souhaitiez placer le traitement des transactions et la sortie du journal avant le même point de jointure. Supposons également que l'ordre d'exécution soit que la transaction soit effectuée absolument en premier. Vous pouvez utiliser l'un des conseils, Avant, pour l'exécuter devant le point de jointure. Cependant, selon le Document officiel Si plusieurs avis du même type existent au même point de jointure, l'ordre d'exécution est indéfini et changera d'une exécution à l'autre.
Si deux conseils définis dans le même aspect doivent être exécutés au même point de jointure, l'ordre est indéfini (car il n'y a aucun moyen d'obtenir l'ordre de déclaration par réflexion dans une classe compilée avec javac). ).
Il y a deux façons de résoudre ce problème: placer la transaction et la sortie du journal dans le même avis, ou le découper dans un autre aspect et utiliser @ Order
.
Ce n'est pas grave s'ils sont inclus dans le même avis et il n'y a pas de problème, mais dans la plupart des cas ce n'est pas le cas, il est donc plus réaliste d'utiliser @ Order
. Vous pouvez utiliser @ Order
pour spécifier l'ordre d'exécution des aspects qui sont dans la même ligne.
En guise de test, ajoutez @ Order (1)
à l'Aspect1 utilisé précédemment, copiez Aspect1 pour créer une classe Aspect2 avec uniquement les caractères de sortie de la console modifiés et ajoutez @ Order (2)
pour l'exécuter.
Aspect1.java
・
・
・
(Omis)
@Aspect
@Component
@EnableAspectJAutoProxy
@Order(1) //Ajouter une annotation
public class Aspect1 {
(Omis)
・
・
・
Aspect2.java
・
・
・
(Omis)
@Aspect
@Component
@EnableAspectJAutoProxy
@Order(2) //Ajouter une annotation
public class Aspect2 { //Une copie d'Aspect1
@Before("execution(* com.example.practiceaop.AopSampleController.helloWorld(..))")
public void before1(JoinPoint jp) {
System.out.println("Before2"); //Changé de Before1 à Before2. Il en va de même pour les conseils ci-dessous.
}
(Omis)
・
・
・
Les résultats sont les suivants. Vous pouvez voir que l'ordre d'exécution n'est pas perturbé entre les aspects 1 et 2.
Avant d'exécuter la méthode Around1
Before1
Avant d'exécuter la méthode Around2
Before2
【Hello World!】
AfterReturning2
After2
Après avoir exécuté la méthode Around2
AfterReturning1
After1
Après avoir exécuté la méthode Around1
Juste au cas où, j'ai ajouté @ Order (2)
à Aspect1 et @Order (1)
à Aspect2.
Comme indiqué ci-dessous, l'ordre d'exécution n'était pas confondu entre les aspects 1 et 2, et seules les priorités des aspects pouvaient être échangées proprement.
Avant d'exécuter la méthode Around2
Before2
Avant d'exécuter la méthode Around1
Before1
【Hello World!】
AfterReturning1
After1
Après avoir exécuté la méthode Around1
AfterReturning2
After2
Après avoir exécuté la méthode Around2
Cette fois, nous avons examiné l'ordre d'exécution de Spring AOP. En particulier, le comportement après le point de jointure n'était pas bien compris, et même si je le recherchais, les résultats n'étaient pas cohérents avec le document, ce qui était assez déroutant. Peut-être que le créateur du document lui-même est déroutant, et il semble que la description devienne plus polie à mesure que la nouvelle version sort ...
J'espère que cet article sera utile à tout le monde. Aussi, si vous avez des erreurs, faites-le moi savoir ...!
Merci pour la lecture!
Recommended Posts