Implementarea unei adnotări AOP personalizate de primăvară

1. Introducere

În acest articol, vom implementa o adnotare AOP personalizată folosind suportul AOP din primăvară.

În primul rând, vom oferi o imagine de ansamblu la nivel înalt a AOP, explicând ce este și avantajele sale. În continuare, vom implementa adnotarea noastră pas cu pas, construind treptat o înțelegere mai aprofundată a conceptelor AOP pe măsură ce mergem.

Rezultatul va fi o mai bună înțelegere a AOP și capacitatea de a crea adnotări personalizate de primăvară în viitor.

2. Ce este o adnotare AOP?

Pentru a rezuma rapid, AOP reprezintă programarea orientată pe aspect. În esență, este o modalitate de a adăuga comportament la codul existent fără a modifica codul respectiv .

Pentru o introducere detaliată a AOP, există articole despre reduceri punctuale și sfaturi AOP. Acest articol presupune că avem deja cunoștințe de bază.

Tipul de AOP pe care îl vom implementa în acest articol este determinat de adnotări. Este posibil să fim deja familiarizați cu acest lucru dacă am folosit adnotarea Spring @Transactional :

@Transactional public void orderGoods(Order order) { // A series of database calls to be performed in a transaction }

Cheia aici este non-invazivitatea. Prin utilizarea meta-datelor de adnotare, logica noastră centrală de afaceri nu este poluată cu codul nostru de tranzacție. Acest lucru face mai ușor să raționeze, să refactorizeze și să testeze izolat.

Uneori, oamenii care dezvoltă aplicații de primăvară pot vedea asta ca Magic de primăvară”, fără să se gândească în detaliu la modul în care funcționează. În realitate, ceea ce se întâmplă nu este deosebit de complicat. Cu toate acestea, odată ce am parcurs pașii din acest articol, vom putea crea propria noastră adnotare personalizată pentru a înțelege și a valorifica AOP.

3. Dependența Maven

În primul rând, să adăugăm dependențele noastre Maven.

Pentru acest exemplu, vom folosi Spring Boot, deoarece abordarea convenției sale asupra configurării ne permite să pornim și să rulăm cât mai repede posibil:

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

Rețineți că am inclus starterul AOP, care trage în bibliotecile de care avem nevoie pentru a începe implementarea aspectelor.

4. Crearea adnotării noastre personalizate

Adnotarea pe care o vom crea este una care va fi utilizată pentru a înregistra cât timp durează o metodă de executat. Să creăm adnotarea noastră:

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

Deși este o implementare relativ simplă, merită menționat pentru ce sunt folosite cele două meta-adnotări.

@Target adnotare ne spune în cazul în care adnotarea noastră va fi aplicabilă. Aici folosim ElementType.Method, ceea ce înseamnă că va funcționa doar pe metode. Dacă am încerca să folosim adnotarea oriunde altundeva, atunci codul nostru nu ar reuși să se compileze. Acest comportament are sens, deoarece adnotarea noastră va fi utilizată pentru timpul de execuție a metodei de înregistrare.

Și @Retention specifică doar dacă adnotarea va fi disponibilă pentru JVM în timpul rulării sau nu. În mod implicit nu este, astfel încât Spring AOP nu va putea vedea adnotarea. Acesta este motivul pentru care a fost reconfigurat.

5. Crearea aspectului nostru

Acum avem adnotarea noastră, să creăm aspectul nostru. Acesta este doar modulul care va încapsula preocuparea noastră transversală, care este cazul nostru este înregistrarea timpului de execuție a metodei. Tot ce este este o clasă, adnotată cu @Aspect:

@Aspect @Component public class ExampleAspect { }

Am inclus și adnotarea @Component , deoarece clasa noastră trebuie să fie, de asemenea, un bob de primăvară pentru a fi detectat. În esență, aceasta este clasa în care vom implementa logica pe care dorim să o injectăm adnotarea noastră personalizată.

6. Crearea punctelor și sfaturilor noastre

Acum, haideți să ne creăm punctele și sfaturile. Aceasta va fi o metodă adnotată care trăiește în aspectul nostru:

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

Din punct de vedere tehnic, acest lucru nu schimbă încă comportamentul nimic, dar se întâmplă încă multe lucruri care necesită analize.

În primul rând, am adnotat metoda noastră cu @Around. Acesta este sfatul nostru, iar în legătură cu sfatul înseamnă că adăugăm cod suplimentar atât înainte, cât și după executarea metodei. Există și alte tipuri de sfaturi, cum ar fi înainte și după, dar acestea vor fi lăsate în afara domeniului de aplicare pentru acest articol.

Apoi, adnotarea noastră @Around are un argument de reducere a punctelor. Punctul nostru indică doar „Aplicați acest sfat oricărei metode care este adnotată cu @LogExecutionTime ”. Există o mulțime de alte tipuri de puncte, dar vor fi lăsate din nou în afara domeniului de aplicare.

Metoda logExecutionTime () în sine este sfatul nostru. Există un singur argument, ProceedingJoinPoint. În cazul nostru, aceasta va fi o metodă de executare care a fost adnotată cu @LogExecutionTime.

În cele din urmă, când metoda noastră adnotată ajunge să fie apelată, ceea ce se va întâmpla este că sfatul nostru va fi numit mai întâi. Apoi depinde de sfatul nostru să decidem ce să facem în continuare. În cazul nostru, sfatul nostru nu face altceva decât să apelăm proceed (), care este doar apelarea metodei originale adnotate.

7. Înregistrarea timpului nostru de execuție

Acum avem scheletul pe loc, tot ce trebuie să facem este să adăugăm o logică suplimentară sfaturilor noastre. Acesta va fi cel care înregistrează timpul de execuție pe lângă apelarea metodei originale. Să adăugăm acest comportament suplimentar la sfaturile noastre:

@Around("@annotation(LogExecutionTime)") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object proceed = joinPoint.proceed(); long executionTime = System.currentTimeMillis() - start; System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms"); return proceed; }

Din nou, nu am făcut nimic deosebit de complicat aici. Tocmai am înregistrat ora curentă, am executat metoda, apoi am imprimat cantitatea de timp necesară în consolă. De asemenea, înregistrăm semnătura metodei, care este furnizată pentru a utiliza instanța joinpoint . De asemenea, am putea avea acces la alte informații dacă dorim, cum ar fi argumentele metodei.

Acum, să încercăm să adnotăm o metodă cu @LogExecutionTime și apoi să o executăm pentru a vedea ce se întâmplă. Rețineți că acesta trebuie să fie Spring Bean pentru a funcționa corect:

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

După executare, ar trebui să vedem următoarele conectate la consolă:

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

8. Concluzie

În acest articol, am valorificat Spring Boot AOP pentru a crea adnotarea noastră personalizată, pe care o putem aplica boabelor Spring pentru a le injecta un comportament suplimentar în timpul rulării.

Codul sursă pentru aplicația noastră este disponibil pe GitHub; acesta este un proiect Maven care ar trebui să poată rula așa cum este.