Spring AOP to nic trudnego jeżeli mamy kawałek fundamentalej wiedzy. Rozbierając na czynniki pierwsze Aspekty i Springa jesteśmy lepiej w stanie pojąć i zrozumieć o co tam chodzi.
W dzisiejszym artykule:
Aspekty i Spring
Programowanie aspektowe (AOP) uzupełnia programowanie obiektowe (OOP), dostarczając innego sposobu myślenia o strukturze programu. Kluczową jednostką modularności programowania obiektowego jest klasa, podczas gdy w AOP jednostką modularności jest aspekt. Aspekty umożliwiają modularyzację zagadnień (takich jak zarządzanie transakcjami), które dotyczą wielu typów i obiektów.
AOP jest paradygmatem programowania, który ma na celu zwiększenie modularności. Czyni to poprzez dodawanie dodatkowych zachowań do istniejącego kodu bez modyfikowania samego kodu. Zamiast tego, możemy zadeklarować nowy kod i nowe zachowanie osobno.
Jednym z kluczowych komponentów Springa jest framework AOP. Podczas gdy kontener Spring IoC nie zależy od AOP (co oznacza, że nie musisz używać AOP, jeśli nie chcesz), AOP uzupełnia Spring IoC, aby zapewnić wydajne rozwiązania. AOP w Springu jest używant m.in. do:
- Dostarczania deklaratywnych usług – najważniejszą taką usługą jest zarządzanie transakcjami.
- Pozwala użytkownikom implementować niestandardowe aspekty, uzupełniając ich użycie OOP o AOP.
Koncept Aspektów
Aby lepiej zrozumieć działanie i odsłonić magię aspektów, trzeba się zapoznać z tym jak ten mechanizm działa. Spójrz na obrazek i przejdź do opisu definicji, aby lepiej poznać konept aspektów.
JoinPoint
Joinpoint jest punktem podczas wykonywania programu, takim jak wykonanie metody lub obsługa wyjątku. W Spring AOP, JoinPoint zawsze reprezentuje wykonanie metody.
Pointcut
Poincut jest predykatem, który pomaga dopasować poradę (Advice), która ma być zastosowana przez Aspekt w konkretnym JoinPoincie. Często kojarzymy Advice z Pointcutem i jest ona uruchamiana w dowolnym JoinPoincie dopasowanym przez ten Poincut.
Advice
Advice jest akcją podejmowaną przez aspekt w konkretnym JoinPoincie. Są różne typy porad, o czym w kolejnym punkcie. W Springu, Advice jest modelowany jako interceptor, utrzymujący łańcuch interceptorów wokół Joinpoint.
Rodzaje Advice w AOP
Spring AOP zawiera następujące rodzaje porad:
- Before advice – porada, która działa przed JoinPointem, ale która nie ma możliwości zatrzymania wykonania programu (chyba, że rzuci wyjątek).
- After returning advice – porada, którą należy uruchomić po normalnym zakończeniu JoinPointu (na przykład, jeśli metoda powróci bez rzucenia wyjątku).
- After throwing advice – porada, którą należy uruchomić, jeśli metoda zakończy działanie rzucając wyjątek.
- After (finally) advice – porada, którą należy uruchomić niezależnie od sposobu, w jaki JoinPoint wychodzi z wykonania (normalny lub zakończony wyjątkiem).
- Around advice – porada, która otacza JoinPoint, taki jak wywołanie metody. Jest to najpotężniejszy rodzaj porady. Wokół porady może wykonywać niestandardowe zachowanie przed i po wywołaniu metody. Jest ona również odpowiedzialna za wybór, czy kontynuować wykonanie do punktu złączenia (JoinPointu), czy też skrócić wykonanie zalecanej metody poprzez zwrócenie własnej wartości lub rzucenie wyjątku.
Mechanizm Proxy
Spring AOP do utworzenia proxy dla danego obiektu docelowego używa albo:
- dynamicznych proxy JDK
- albo CGLIB
Dynamiczne proxy JDK są wbudowane w JDK, podczas gdy CGLIB jest powszechną biblioteką definicji klas o otwartym kodzie źródłowym (przepakowaną do spring-core).
Jeśli obiekt docelowy, który ma być proxowany, implementuje przynajmniej jeden interfejs, używane jest dynamiczne proxy JDK. Natomiast jeżeli obiekt docelowy nie implementuje żadnych interfejsów, tworzone jest proxy przez CGLIB.
Przykład użycia
Zaczynamy od napisania Join Pointu – czyli w przypadku springa będzie to jakaś metoda.
1 2 3 4 5 6 | @Component public class BusinessClass{ public void validateTransaction() { System.out.println("...validating"); } } |
Definijuemy Aspect, Pointcuty oraz Advice’y. Pointcuty można definiować oddzielnie a później wykorzystywać jako ’metodę’ w Advice.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @Aspect @Component public class LoggingAspect { @Pointcut("execution(* BusinessClass.validateTransaction(..))") public void logAfter() { //pointcut } @Before("execution(* BusinessClass.validateTransaction())") //point-cut expression public void logBefore(JoinPoint joinPoint) { System.out.println("BusinessClass.logBefore() : " + joinPoint.getSignature().getName()); } @AfterReturning(pointcut = "logAfter()") public void logujWyjscieZUslugi(JoinPoint joinPoint) { System.out.println("BusinessClass.afterRetuning() : " + joinPoint.getSignature().getName()); } } |
Po wywołaniu metody, na którą Aspekt był założony wynik naszego programu jest następujący:
1 2 3 | BusinessClass.logBefore() : validateTransaction ...validating BusinessClass.afterRetuning() : validateTransaction |
Podsumowanie
Mechanizm AOP z pewnością jest jednym z bardziej zaawansowanych technik programowania, która w pewnym momencie kariery staje się niezbędna. Aspekty pozwalają nam na wszechstronność pisania kodu. Za ich pomocą możemy bezinwazyjnie m.in. dodać logi audytu, lub transakcyjność. Jednak z doświadczeniem przychodzi pewna pokora i chciałbym ustrzec wszystkich podjaranych, że czytelność i utrzymanie kodu jest ważniejsze niż kod napisany fajnie. Z tego powodu raczej odradzam dodawanie biznes case’ów w postaci aspektów. Pamiętaj, ten kod ktoś musi później utrzymać.
Źródła:
- https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop
- https://howtodoinjava.com/spring-aop-tutorial/
- https://www.baeldung.com/spring-aop
- https://blog.allegro.tech/2014/12/aspects-in-spring.html
Myślę że warto było by wspomnieć, że: aspekt się załączy tylko jeśli metoda jest wywoływana przez beana (obiekt proxy), oraz że metoda będąca Join Point musi być publiczna.