Een aangepaste Spring AOP-annotatie implementeren

1. Inleiding

In dit artikel zullen we een aangepaste AOP-annotatie implementeren met behulp van de AOP-ondersteuning in het voorjaar.

Eerst geven we een algemeen overzicht van AOP, waarin we uitleggen wat het is en wat de voordelen ervan zijn. Hierna zullen we onze annotatie stap voor stap implementeren en geleidelijk een dieper begrip van AOP-concepten opbouwen.

Het resultaat is een beter begrip van AOP en de mogelijkheid om in de toekomst onze eigen lente-annotaties te maken.

2. Wat is een AOP-annotatie?

Kort samengevat staat AOP voor aspectgericht programmeren. Eigenlijk, het is een manier om gedrag toe te voegen aan bestaande code zonder die code te wijzigen.

Voor een gedetailleerde inleiding tot AOP zijn er artikelen over AOP-punten en advies. In dit artikel wordt ervan uitgegaan dat we al een basiskennis hebben.

Het type AOP dat we in dit artikel zullen implementeren, is annotatiegestuurd. We kennen dit misschien al als we de lente hebben gebruikt @Transactional annotatie:

@Transactional public void orderGoods (Order order) {// Een reeks databaseaanroepen die in een transactie moeten worden uitgevoerd}

De sleutel hier is niet-invasiviteit. Door annotatie-metadata te gebruiken, wordt onze core business logica niet vervuild met onze transactiecode. Dit maakt het gemakkelijker om te redeneren, te refactoren en afzonderlijk te testen.

Soms kunnen mensen die Spring-applicaties ontwikkelen dit zien alsSpring Magic ', zonder in detail na te denken over hoe het werkt. In werkelijkheid is wat er gebeurt niet bijzonder ingewikkeld. Zodra we de stappen in dit artikel hebben voltooid, kunnen we echter onze eigen aangepaste annotatie maken om AOP te begrijpen en te gebruiken.

3. Maven Afhankelijkheid

Laten we eerst onze Maven-afhankelijkheden toevoegen.

Voor dit voorbeeld zullen we Spring Boot gebruiken, aangezien de conventie boven de configuratiebenadering ons zo snel mogelijk aan de slag laat:

 org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE org.springframework.boot spring-boot-starter-aop 

Merk op dat we de AOP-starter hebben toegevoegd, die de bibliotheken binnenhaalt die we nodig hebben om aspecten te implementeren.

4. Onze aangepaste annotatie maken

De annotatie die we gaan maken, is er een die zal worden gebruikt om de hoeveelheid tijd te registreren die een methode nodig heeft om uit te voeren. Laten we onze annotatie maken:

@Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) openbaar @interface LogExecutionTime {} 

Hoewel het een relatief eenvoudige implementatie is, is het vermeldenswaard waarvoor de twee meta-annotaties worden gebruikt.

De @Doelwit annotatie vertelt ons waar onze annotatie van toepassing zal zijn. Hier gebruiken we ElementType.Method, wat betekent dat het alleen op methoden werkt. Als we de annotatie ergens anders zouden proberen te gebruiken, zou onze code niet compileren. Dit gedrag is logisch, aangezien onze annotatie zal worden gebruikt voor het loggen van de uitvoeringstijd van de methode.

En @Retentie geeft alleen aan of de annotatie tijdens runtime beschikbaar zal zijn voor de JVM of niet. Standaard is dit niet het geval, dus Spring AOP zou de annotatie niet kunnen zien. Dit is waarom het opnieuw is geconfigureerd.

5. Ons aspect creëren

Nu we onze aantekening hebben, laten we ons aspect creëren. Dit is slechts de module die onze transversale zorg zal inkapselen, wat in ons geval het loggen van de uitvoering van de methode is. Het is alleen een klasse, met annotaties @Aspect:

@Aspect @Component openbare klasse ExampleAspect {}

We hebben ook de @Component annotatie, omdat onze klas ook een Spring bean moet zijn om te worden gedetecteerd. In wezen is dit de klasse waarin we de logica zullen implementeren die we onze aangepaste annotatie willen laten injecteren.

6. Creëren van onze pointcut en advies

Laten we nu onze punt en advies maken. Dit zal een geannoteerde methode zijn die leeft in ons aspect:

@Around ("@ annotation (LogExecutionTime)") public Object logExecutionTime (ProceedingsJoinPoint joinPoint) gooit Throwable {return joinPoint.proceed (); }

Technisch gezien verandert dit het gedrag van niets nog, maar er is nog heel wat aan de hand dat moet worden geanalyseerd.

Ten eerste hebben we onze methode geannoteerd met @In de omgeving van. Dit is ons advies, en rond advies betekent dat we extra code toevoegen, zowel voor als na de uitvoering van de methode. Er zijn andere soorten advies, zoals voordat en na maar ze worden buiten het bereik van dit artikel gelaten.

Vervolgens onze @In de omgeving van annotatie heeft een point cut-argument. Onze pointcut zegt gewoon: ‘Pas dit advies toe op elke methode die is geannoteerd @LogExecutionTime. ' Er zijn veel andere soorten pointcuts, maar deze zullen weer worden weggelaten als scope.

De methode logExecutionTime () zelf is ons advies. Er is een enkel argument, Doorgaan met lid worden. In ons geval is dit een uitvoeringsmethode die is geannoteerd met @LogExecutionTime.

Ten slotte, wanneer onze geannoteerde methode wordt aangeroepen, zal wat er zal gebeuren, ons advies als eerste worden aangeroepen. Dan is het aan ons advies om te beslissen wat we nu gaan doen. In ons geval doet ons advies niets anders dan bellen doorgaan(), dat is het gewoon aanroepen van de originele geannoteerde methode.

7. Onze uitvoeringstijd registreren

Nu we ons skelet hebben, hoeven we alleen maar wat extra logica aan ons advies toe te voegen. Dit is wat de uitvoeringstijd registreert, naast het aanroepen van de oorspronkelijke methode. Laten we dit extra gedrag aan ons advies toevoegen:

@Around ("@ annotation (LogExecutionTime)") openbaar object logExecutionTime (ProceedingsJoinPoint joinPoint) gooit Throwable {lange start = System.currentTimeMillis (); Object gaat verder = joinPoint.proceed (); lange executietijd = System.currentTimeMillis () - start; System.out.println (joinPoint.getSignature () + "uitgevoerd in" + executionTime + "ms"); terug te gaan; }

Nogmaals, we hebben hier niets gedaan dat bijzonder gecompliceerd is. We hebben zojuist de huidige tijd geregistreerd, de methode uitgevoerd en vervolgens de hoeveelheid tijd die het kostte op de console afgedrukt. We loggen ook de methodehandtekening, die wordt verstrekt om de verbindingspunt voorbeeld. We zouden ook toegang kunnen krijgen tot andere stukjes informatie als we dat zouden willen, zoals methode-argumenten.

Laten we nu eens proberen een methode te annoteren met @LogExecutionTime, en vervolgens uitvoeren om te zien wat er gebeurt. Merk op dat dit een Spring Bean moet zijn om correct te werken:

@LogExecutionTime public void serve () gooit InterruptedException {Thread.sleep (2000); }

Na uitvoering zouden we het volgende op de console moeten zien gelogd:

void org.baeldung.Service.serve () uitgevoerd in 2030ms

8. Conclusie

In dit artikel hebben we Spring Boot AOP gebruikt om onze aangepaste annotatie te maken, die we kunnen toepassen op Spring Beans om ze tijdens runtime extra gedrag te geven.

De broncode voor onze applicatie is beschikbaar op meer dan op GitHub; dit is een Maven-project dat zou moeten kunnen draaien zoals het is.