[Java] Spring AOP-Ausführungsreihenfolge

Einführung

Dieses Mal werde ich über die Ausführungsreihenfolge von Spring AOP schreiben. Wir zeigen Ihnen, wie Sie Ratschläge innerhalb desselben Aspekts priorisieren und wie Sie denselben Ratschlag zwischen verschiedenen Aspekten priorisieren!

Eine kurze Erklärung zu Spring AOP selbst finden Sie unter Qiita (First Spring AOP). Wenn Sie also mehr über AOP erfahren möchten, lesen Sie bitte hier. Es wird glatt sein, wenn Sie es nach Erhalt lesen.

Nutzungsumgebung und Version

Beratungspriorität

Es gibt eine Priorität in der Reihenfolge, in der die Beratung ausgeführt wird. [^ 1]

  1. @Around
  2. @Before
  3. @After
  4. @AfterReturning
  5. @AfterThrowing

[^ 1]: Da es sich um ein Dokument der Spring-Core 5.3-Serie handelt, unterscheidet es sich geringfügig von der diesmal verwendeten Version (5.2.6), 5.2.6 gibt jedoch nicht die Priorität von Advice an Da es keine Diskrepanz zu den später beschriebenen experimentellen Ergebnissen gab, bezog ich mich nur für diesen Teil auf die 5.3-Serie. https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/core.html#aop-ataspectj-advice-ordering

Das heißt, wenn "@ Around" und "@ Before" im selben Joinpoint vorhanden sind, wird zuerst der in "@ Around" definierte Vorausführungsprozess ausgeführt. Was ist, wenn es "@ Before", "@ After", "@ AfterReturning" und "@ Around" gibt, die vor und nach der Ausführung des Joinpoints am selben Joinpoint im selben Aspekt verarbeitet werden?

Ich habe mit einem einfachen HelloWorld-Code experimentiert, der aus den folgenden 3 Klassen besteht.

  1. Hauptklasse
  2. Controller-Klasse
  3. Die Klasse, die den Aspekt definiert

Zu jedem wird eine kurze Einführung und ein Quellcode bereitgestellt.

① Hauptklasse Eine Klasse, die Spring einfach ausführt. Es erfolgt keine spezielle Verarbeitung. Da "@ EnableAspectJAutoProxy" eine Anmerkung ist, die zum Aktivieren von AOP erforderlich ist, wird sie diesmal allen Klassen hinzugefügt.

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);
	    }
}

② Controller-Klasse Klicken Sie auf http: // localhost: 8080 /, um [Hello World!] Auf der Konsole anzuzeigen. Ich habe den Code aus Qiita-Artikel, den ich zuvor geschrieben habe ziemlich oft verwendet (ich bin froh, dass ich ihn gemacht habe!).

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!】");
    }
}

③ Klasse, die den Aspekt definiert Der Join-Punkt ist helloWorld () von AopSampleController, der allen Ratschlägen gemeinsam ist.

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("Vor dem Ausführen der Around1-Methode");
		try {
			Object result = pjp.proceed();
			System.out.println("Nach dem Ausführen der Around1-Methode");
			return result;
		} catch (Throwable e) {
			throw e;
		}
	}
}


Das Ausführungsergebnis ist wie folgt.

Vor dem Ausführen der Around1-Methode
Before1
【Hello World!】
AfterReturning1
After1
Nach dem Ausführen der Around1-Methode

Warum ist die Ausführungsreihenfolge nach dem Verknüpfungspunkt am niedrigsten, obwohl die Priorität von Around die erste sein sollte? [Offizielle Dokumentation](https://docs.spring.io/spring-framework/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-ataspectj-advice-ordering] zum besseren Verständnis ) Wird von Deepl Translate ins Japanische übersetzt (das Erscheinungsbild wird ein wenig manuell angepasst).

Spring AOP folgt denselben Prioritätsregeln wie AspectJ, um die Reihenfolge zu bestimmen, in der die Beratung ausgeführt wird. Der mit der höchsten Priorität wird zuerst ausgeführt (dh wenn es zwei Voranweisungen gibt, wird der mit der höchsten Priorität zuerst ausgeführt). Auf dem Weg aus dem Join-Punkt wird der Hinweis mit der höheren Priorität zuletzt ausgeführt (wenn also zwei nach dem Hinweis gegeben werden, wird die höhere Priorität als zweiter ausgeführt).

"Auf dem Weg aus dem Verbindungspunkt wird der Rat mit hoher Priorität zuletzt ausgeführt", was verwirrend ist. Mit anderen Worten, bevor die Verbindungspunktmethode ausgeführt wird, ist die Priorität gleich der Ausführungsreihenfolge, aber nachdem die Verbindungspunktmethode ausgeführt wurde, wird diejenige mit der niedrigsten Priorität zuerst ausgeführt.

Priorisierung bei gleicher Leitung

Angenommen, Sie möchten die Transaktionsverarbeitung und die Protokollausgabe vor denselben Verknüpfungspunkt stellen. Angenommen, die Ausführungsreihenfolge lautet, dass die Transaktion absolut zuerst ausgeführt wird. Sie können einen der Hinweise "Vorher" verwenden, um ihn vor dem Verknüpfungspunkt auszuführen. Gemäß den offiziellen Dokumenten Wenn am selben Verknüpfungspunkt mehrere Hinweise desselben Typs vorhanden sind, ist die Ausführungsreihenfolge unbestimmt und ändert sich von Ausführung zu Ausführung.

Wenn zwei im selben Aspekt definierte Ratschläge am selben Verknüpfungspunkt ausgeführt werden müssen, ist die Reihenfolge unbestimmt (da es keine Möglichkeit gibt, die Deklarationsreihenfolge durch Reflektion in einer mit javac kompilierten Klasse zu erhalten). ).

Es gibt zwei Möglichkeiten, dies zu lösen: Fügen Sie die Transaktions- und Protokollausgabe in denselben Rat ein oder schneiden Sie sie in einen anderen Aspekt aus und verwenden Sie "@ Order". Es ist in Ordnung, wenn sie im selben Rat enthalten sind und es kein Problem gibt, aber ich denke, dass dies in den meisten Fällen nicht der Fall ist. Daher ist es realistischer, "@ Order" zu verwenden. Sie können "@ Order" verwenden, um die Ausführungsreihenfolge von Aspekten anzugeben, die sich in derselben Zeile befinden.

Fügen Sie als Test "@ Order (1)" zu dem zuvor verwendeten Aspect1 hinzu, kopieren Sie Aspect1, um eine Aspect2-Klasse zu erstellen, bei der nur die Konsolenausgabezeichen geändert wurden, und fügen Sie "@ Order (2)" hinzu, um sie auszuführen.

Aspect1.java


・
・
・
(Weggelassen)
@Aspect
@Component
@EnableAspectJAutoProxy
@Order(1) //Anmerkung hinzufügen
public class Aspect1 {
(Weggelassen)
・
・
・

Aspect2.java


・
・
・
(Weggelassen)
@Aspect
@Component
@EnableAspectJAutoProxy
@Order(2) //Anmerkung hinzufügen
public class Aspect2 { //Eine Kopie von Aspect1
@Before("execution(* com.example.practiceaop.AopSampleController.helloWorld(..))")
	public void before1(JoinPoint jp) { 
		System.out.println("Before2"); //Geändert von Before1 zu Before2. Gleiches gilt für die nachstehenden Hinweise.
	}
(Weggelassen)
・
・
・

Die Ergebnisse sind wie folgt. Sie sehen, dass die Ausführungsreihenfolge nicht zwischen Aspekt 1 und 2 verwechselt wird.

Vor dem Ausführen der Around1-Methode
Before1
Vor dem Ausführen der Around2-Methode
Before2
【Hello World!】
AfterReturning2
After2
Nach dem Ausführen der Around2-Methode
AfterReturning1
After1
Nach dem Ausführen der Around1-Methode

Nur für den Fall, ich habe "@ Order (2)" zu Aspect1 und "@Order (1)" zu Aspect2 hinzugefügt. Wie unten gezeigt, wurde die Ausführungsreihenfolge zwischen den Aspekten 1 und 2 nicht verwechselt, und nur die Prioritäten der Aspekte konnten sauber ausgetauscht werden.

Vor dem Ausführen der Around2-Methode
Before2
Vor dem Ausführen der Around1-Methode
Before1
【Hello World!】
AfterReturning1
After1
Nach dem Ausführen der Around1-Methode
AfterReturning2
After2
Nach dem Ausführen der Around2-Methode

abschließend

Dieses Mal haben wir uns die Ausführungsreihenfolge von Spring AOP angesehen. Insbesondere das Verhalten nach dem Verknüpfungspunkt wurde nicht gut verstanden, und selbst wenn ich danach suchte, stimmten die Ergebnisse nicht mit dem Dokument überein, was ziemlich verwirrend war. Vielleicht ist der Dokumentersteller selbst verwirrend und es scheint, dass die Beschreibung mit der neuen Version höflicher wird ...

Ich hoffe, dieser Artikel ist für jedermann hilfreich. Wenn Sie Fehler haben, lassen Sie es mich bitte wissen ...!

Danke fürs Lesen!

Recommended Posts

[Java] Spring AOP-Ausführungsreihenfolge
Java-Tipps - Zusammenfassung der Federausführung
Frühling Java
Über Spring AOP
[Java] Spring DI ③
Über Spring AOP Pointcut
Übersicht über Spring AOP
Parallele Ausführung in Java
[Java] Spring AOP-Ausführungsreihenfolge
Java-Tipps - Zusammenfassung der Federausführung
Spring + Gradle + Java Schnellstart
Java-Debug-Ausführung [für Java-Anfänger]
[Java] Thymeleaf Basic (Spring Boot)
Einführung in Spring Boot ② ~ AOP ~
CICS-Java-Anwendung ausführen- (4) Spring Boot-App
Verwenden von Mapper mit Java (Spring)
[Java] Wie Spring DI funktioniert
Externe Prozessausführung in Java
[Java] [Spring] Spring Boot 1.4-> 1.2 Downgrade Hinweis
So führen Sie einen Komponententest für Spring AOP durch
Java Spring-Umgebung in vs Code
Frühlingsrahmen Einfaches Studienmemo (2): AOP
Elastische Bohnenstange (Java) + Spring Boot + https
Über das Binden der Spring AOP Annotation
Implementieren Sie reCAPTCHA v3 in Java / Spring
Spring Framework-Tools für Java-Entwickler
[Java] Spring DI ④ - Lebenszyklusverwaltung
[Java] LINE-Integration mit Spring Boot
IDE (Eclipse) Debug-Ausführung, Schrittausführung (Java)
Spring AOP zum ersten Mal