spring aop

Spring AOP – jak używać?

Programowanie zorientowane aspektowo (z ang. aspect-oriented programming – w springu jest to moduł Spring AOP) to sposób programowania, który pozwala oddzielić pewne fragmenty kodu od siebie, poprawiając dzięki temu modularność tworzonego programu. Można na to spojrzeć tak, że programowanie aspektowe pozwala wprowadzić dodatkową warstwę do kodu, który tworzymy i zwykle ta warstwa jest wyraźnie odseparowana od właściwego kodu.

 

Jak działają aspekty?

Aspekty działają poprzez dodanie klasy proxy do właściwego wywołania metody z danej klasy.

Normalne wywołanie metody z osobnej klasy wygląda mniej więcej tak:

normalne wywołanie klasy

 

Mechanizm AOP dodaje pomiędzy klasą klienta a klasą właściwą dodatkową klasę proxy. Tak wygląda to w uruchomionej aplikacji (runtime):

Wywołanie klasy w Spring AOP

 

Dzięki aspektom nie musimy ręcznie tworzyć klas proxy, mechanizm AOP zrobi to za nas. Jest o tyle dobre, że taki aspekt możemy dodać na kilka wygodnych sposobów, np. poprzez adnotacje, poprzez odpowiedni pointcut (czyli predykat, określający w którym miejscu dany aspekt ma zostać „podłączony”).

 

W kodzie natomiast, nasz aspekt jest tak jakby trochę z boku właściwego kodu i możemy go przedstawić na symbolicznym diagramie w taki sposób:

Wywołanie klas wraz z aspektami(prezentacja w kodzie)

 

Jak uruchomić aspekty ze Spring AOP

W Springu są dwie możliwości korzystania z aspektów: jest to podejście XMLowe i AspectJ. Ponieważ podejście XMLowe jest przydatne tylko w kilku nielicznych przypadkach, większość developerów korzysta z podejścia AspectJ. Dlatego przedstawię tutaj przykłady dla AspectJ.

Przede wszystkim trzeba w odpowiedni sposób skonfigurować korzystanie z AspectJ. Ja w aplikacji spring bootowej dodaję odpowiednią zależność.

implementation 'org.springframework.boot:spring-boot-starter-aop'

 

Następnie w głównej klasie aplikacji dodaję odpowiednią adnotację.

@EnableAspectJAutoProxy(proxyTargetClass=true)

 

Parametr proxyTargetClass=true odpowiada za to, że mechanizm AOP będzie mógł utworzyć klasę proxy dla klasy. Domyślnie AOP w Springu tworzy proxy korzystając z mechanizmu JDK dynamic proxies. Mechanizm ten tworzy proxy na podstawie interfejsu. Jeśli nie chcesz tworzyć dodatkowo interfejsu wystarczy, że ustawisz proxyTargetClass=true. Dzięki temu ustawieniu Spring AOP tworzy klasę proxy z użyciem biblioteki CGLIB.

 

Jak korzystać ze Spring AOP?

Mamy jakiś dowolny komponent. Może to być klasa oznaczona adnotacją @Component, @Service, @Controller, @Repository (ogólnie bean springowy).

package pl.nullpointerexception.aspect; 
// ... imports ...

@Componet
class MyBaseClass {

  public void doSomething() {
    System.out.println("doSomething");
  }
}

 

Powiedzmy, że chcemy zastosować aspect do wywołania metody doSomething(). Nasz aspekt będzie wyglądał mniej więcej tak:

package pl.nullpointerexception.aspect; 
// ... imports ...

@Aspect // 1
@Component
public class LoggingAspect {

  @Around("execution(* pl.nullpointerexception.aspect..*(..))") // 2
  public Object doSomethingAdvice(ProceedingJoinPoint joinPoint) 
      throws Throwable { // 3
    System.out.println(":: start ::"); // 4
    Object proceed = joinPoint.proceed(); // 5
    System.out.println(":: end ::"); // 6
    return proceed; // 7
  }
}
  1. Aspekt oznaczamy adnotacjami @Aspect i @Component
  2. Adnotacja @Around służy do uruchomienia apektu dookoła wywołania metody (jako parametr zawiera  poincat)
  3. Nazwa metody jest dowolna, jako parametr przyjmuje ona ProceedingJoinPoint, czyli miejsce w którym następuje wywołanie metody lub rzucenie wyjątku. I jest to tzw. Advice
  4. Tutaj umieszczamy kod, który chcemy wykonać przed wywołaniem metody
  5. Właściwe wywołanie metody
  6. Kod, który chcemy wykonać po uruchomieniu metody.
  7. Zwrócenie wyniku z danej metody. Tutaj kończy się wywołanie aspektu i sterowanie zostaje przekazane z powrotem do miejsca, w którym została wywołana metoda doSomething()

 

Wyrażenie execution(* pl.nullpointerexception.aspect..*(..))") to tzw. Pointcut, który odpowiada za odpalenie metody z aspektu „dookoła” każdej publicznej metody w pakiecie pl.nullpointerexception.aspect. Pointcut jest warunkiem, który określa nam gdzie nasz aspekt zostanie uruchomiony. Mogą one przyjmować przeróżne formy. Więcej możesz przeczytać w dokumentacji tutaj. Poza Adnotacją @Around są jeszcze dostępne adnotacje @Before i @After, które pozwalają uruchomić aspekt przed i po wywołaniu metody.

Wywołanie w kontrolerze:

package pl.nullpointerexception.aspect;
// ... imports ...

@RestController
class ControllerClass {
  
  @Autowire
  private MyBaseClass baseClass;

  @GetMapping("/doSomething")
  public void doSomething() {
    baseClass.doSomething();
  }
}

 

Gdy wywołamy metodę doSomething na konsoli zostanie wydrukowane:

:: start ::
doSomething
:: end ::

 

Do czego można używać Aspektów

I tutaj pojawia się najważniejsza kwestia związana z aspektami. Do czego można używać aspektów, a gdzie nie powinniśmy ich stosować?

Techniczny kod, który nie powinien być zmieszany z kodem biznesowym

Klasycznym przykładem użycia AOP są transakcje bazodanowe w Springu. Wystarczy, że dodamy odpowiednią adnotację @Transactional na metodzie lub na klasie i już mamy włączony mechanizm transakcji. Dzięki temu unikamy dopisywania kodu, który jest odpowiedzialny za rozpoczynanie transakcji, commitowanie jej oraz rollback. W ten sposób pozbywamy się z naszego kodu biznesowego kodu, który ma charakter bardziej techniczny. Na podobnej zasadzie możemy wydzielić kod techniczny w innych miejscach.

 

Wszelkiego rodzaju loggery

Aspekty pomagają w tworzeniu mechanizmów logujących, które mogą audytować różnego rodzaju elementy aplikacji. Zwykle są to logi dotyczące użytkownika aplikacji.

Można także w ten sposób zaimplementować prosty mechanizm mierzący czasy wywołań poszczególnych metod (oczywiście są to tego lepsze narzędzia, ale na własny użytek taki mechanizm może nam wystarczyć).

 

Dodatkowe mechanizmy zabezpieczające (Security)

Różnego rodzaju mechanizmy, które pomagają nam zabezpieczać aplikację lub jej poszczególne endpointy. Przy pomocy aspektów możemy zrealizować podwójne uwierzytelnienie. Gdy chcesz zmienić np. email w jakiejś aplikacji. Po przejściu na odpowiednią stronę aplikacja prosi Cię o ponowne zalogowanie się, mimo że jesteś już zalogowany (można zrealizować to za pomocą aspektów).

Kolejny przykład to logowanie „na dwie ręce”. W systemach bankowych, niektóre operacje np. zmiany danych osobowych klienta, może dokonać każdy przedstawiciel klienta, ale żeby je zatwierdzić musi poprosić swojego przełożonego, by ten potwierdził tę operację swoim loginem i hasłem. Takie kolejne logowanie na danym endponcie można zaimplementować właśnie przy użyciu aspektów. Wtedy logika związana z logowaniem nie miesza się nam z logiką zapisu danych klienta.

Jeśli znasz jakieś ciekawe przypadki użycia aspektów to zachęcam do podzielenia się w komentarzach 😉

 

Jak nie używać aspektów

Nie należy ich przede wszystkim nadużywać. Zbyt dużo aspektów sprawi, że kod będzie trudny do czytania. Część logiki będzie poukrywana w aspektach i z poziomu klasy kontrolera, czy serwisu nie będzie po prostu widoczna. Najlepszym chyba rozwiązaniem jest podłączanie aspektów przez stosowanie własnych adnotacji, wtedy w kodzie danej klasy widać tę adnotację i łatwiej jest stwierdzić, że jest jakaś dodatkowa logika, której na pierwszy rzut oka nie widać.

 

Podsumowanie

Aspect oriented programming to ciekawa technika, która może nam pomagać w wielu sytuacjach. Może być dopełnieniem do tradycyjnie stosowanych technik. Trzeba jednak pamiętać, że nadużywanie jej może prowadzić do różnego rodzaju nieporozumień. AOP, tak jak inne techniki, trzeba stosować z głową.

Artykuł pochodzi z cyklu poświęconego Spring Framework, gdzie znajdziesz więcej artykułów dotyczących najpopularniejszych zagadnień w Spring Framework.

 

Żródła:

https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop

 

Mateusz Dąbrowski

Cześć jestem Mateusz, zajmuję się programowaniem już ponad 12 lat z czego ponad 8 programuję w Javie. Zapraszam Cię do lektury mojego bloga. Możesz przeczytać więcej o mnie >>TUTAJ<<

7 thoughts to “Spring AOP – jak używać?”

  1. Czy istnieje jakiś lekki framework do Javy, coś lżejszego niż Spring Boot? Tak jak w Pythonie jest mulaste Django i lekki Flask.

  2. Czy dobrą praktyką jest używanie aspektów do liczenia wejść na dany serwis ew. na dany endpoint.

    1. Endpointy mogą być wywoływane równolegle, więc musiałbyś synchronizować ten licznik, a jak chcesz go aktualizować w bazie czy gdzieś indziej to jeszcze gorzej. Chyba więcej problemów by z tego wynikło. Ale da się to przy użyciu aspektów zrobić, wszystko zależy od implementacji. Jak zrobisz to dobrze to ok, jak nie to wiadomo 😉

      Jeżeli potrzebujesz coś więcej, to może warto zainteresować się metrykami i actuatorem. Ewentualnie poszukaj być może, ktoś już to rozwiązał w jakiś sposób.

      Ja nigdy nie musiałem zliczać ilości wejść a raczej inne parametry: pamięć, procesor, aktywność gc. Do tego używałem actuatora i zbierałem te metryki przez Prometeusza i wystawiałem w Grafanie.

    2. A może warto z innej strony zaatakować? Np Google Analytics do zliczania wejść na stronę?

      1. Jeśli masz api to ciężko jest podłączyć GA. Bo bazuje ono na ciasteczkach (śledzących), większość klientów http domyślnie nie ma włączonej obsługi ciasteczek. A z api może korzystać wielu klientów, więc śledzenie wywołań tylko poprzez GA w aplikacji może być mało miarodajne. Poza tym GA daje statystyki typo biznesowe. A ilość wywołań danego endpointu to raczej metryka typowo techniczna, która może Ci posłużyć np. do skalowania danego serwisu. Oczywiście tam gdzie jest możliwość warto skorzystać z GA.

Komentarze są zamknięte.