W pierwszym poście na temat spring aop wspomniałem o typach advice-ów :
Typy advice-ów :
Rozpatrzmy taki oto przypadek:
Niech dany będzie interfejs :
A co jeśli z metody poleci wyjątek na który mamy advice? Zmodyfikujmy metodę:
Myślę, że warto rozważyć
Chciałbym jeszcze Czytelnikowi zwrócić uwagę, że do tej pory wszystkie moje advice-y były metodami publicznymi zwracającymi void. Nie ma znaczenia ich widoczność oraz co zwracają, z wyjątkiem advice typu around. Otóż w tym przypadku zwracany obiekt to jest obiekt zwracany przez metodę proceed() - czyli w konsekwencji na obiekcie target. Trzeba o tym pamiętać. Rzućmy okiem jeszcze raz na tą metodę:
Tyle w temacie typów advice-ów w spring aop. Stąd można pobrać źródła projektu.
Typy advice-ów :
- before - advice uruchomi się przed wykonaniem join point-a.
- afterReturning - advice uruchomi się po poprawnym wykonaniu join point-a.
- afterThrowing - advice uruchomi się po rzuceniu wyjątku z join point-a.
- after - advice uruchomi się niezależnie od tego czy metoda wykonała się bezbłędnie czy nie.
- around - advice uruchomi się po before, sami tutaj odpowiadamy za wywołanie join point-a i zwrócenie wartości.
Rozpatrzmy taki oto przypadek:
Niech dany będzie interfejs :
public interface Service { String getMessage(String name); void getMessageAndReturnVoid(String name); }Plus prosta implementacja logująca wykonanie się metody:
public class ExampleService implements Service { private static final Logger logger = Logger.getLogger(ExampleService.class); @Override public String getMessage(String name) { logger.info("metoda getMessage"); return name; } @Override public void getMessageAndReturnVoid(String name) { logger.info("metoda getMessageAndReturnVoid"); } }Oraz aspekt:
@Aspect public class LoggerAspect { private static final Logger logger = Logger.getLogger(LoggerAspect.class); @Before("stringParameter()") public void before(JoinPoint joinPoint) { logger.info("Before advice"); } @AfterReturning(pointcut="stringParameter()", returning="value") public void afterReturning(JoinPoint joinPoint, String value) { logger.info("AfterReturning advice zwraca wartość : " + value); } @AfterThrowing(pointcut="stringParameter()", throwing="ex") public void afterThrowing(JoinPoint joinPoint, RuntimeException ex) { logger.info("AfterThrowing advice łapie wyjątek : " + ex); } @After("stringParameter()") public void after(JoinPoint joinPoint) { logger.info("After advice "); } @Around("stringParameter()") public Object around (ProceedingJoinPoint joinPoint) throws Throwable { logger.info("Around advice"); return joinPoint.proceed(); } @Pointcut("execution(* pl..Service+.get*(String))") private void stringParameter() {} }Mamy pointcut łąpiący się na obie metody w interfejsie oraz advice-y wszystkich wymienionych typów. Odpalmy springa, obie metody i spójrzmy na konsolę :
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/app-context.xml"); System.out.println(); Service service = (Service) context.getBean("service"); service.getMessage("badamy typy advice-ów"); System.out.println(); service.getMessageAndReturnVoid("badamy typy advice-ów"); }Moja konsola daje wynik:
INFO [main] (LoggerAspect.java:22) - Before advice INFO [main] (LoggerAspect.java:42) - Around advice INFO [main] (ExampleService.java:11) - metoda getMessage INFO [main] (LoggerAspect.java:37) - After advice INFO [main] (LoggerAspect.java:27) - AfterReturning advice zwraca wartość : badamy typy advice-ów INFO [main] (LoggerAspect.java:22) - Before advice INFO [main] (LoggerAspect.java:42) - Around advice INFO [main] (ExampleService.java:17) - metoda getMessageAndReturnVoid INFO [main] (LoggerAspect.java:37) - After advice INFO [main] (LoggerAspect.java:27) - AfterReturning advice zwraca wartość : null
Możemy odczytać kolejność uruchamiania się odpowiednich advice-ów:
- before.
- around (tutaj sami odpalamy metodę na obiekcie target poprzez joinPoint.proceed()).
- after.
- afterReturning.
A co jeśli z metody poleci wyjątek na który mamy advice? Zmodyfikujmy metodę:
@Override public String getMessage(String name) { logger.info("metoda getMessage"); throw new NullPointerException(); }Bez większego zaskoczenia mam na konsoli:
INFO [main] (LoggerAspect.java:22) - Before advice INFO [main] (LoggerAspect.java:42) - Around advice INFO [main] (ExampleService.java:11) - metoda getMessage INFO [main] (LoggerAspect.java:37) - After advice INFO [main] (LoggerAspect.java:32) - AfterThrowing advice łapie wyjątek : java.lang.NullPointerException Exception in thread "main" java.lang.NullPointerException
...Czyli kolejność jest identyczna jak w pierwszym przypadku, z tym że zamiast afterReturning mamy afterThrowing. Zauważmy również, że deklaracja wyjątku na który się łąpiemy na RuntimeException oznacza również wszystkie jego podtypy (w tym przypadku NullPointerException).
Myślę, że warto rozważyć
- around
- jeśli chcemy implementować cache (nie musimy przecież uruchamiać joinPoint.proceed() a wartość zwrócić skądinąd),
- jeśli chcemy implementować coś w rodzaju kilkukrotnych prób uruchomienia metody. Powiedzmy, że mamy wymaganie biznesowe, które mówi, że należy co najmniej 5-cio krotnie uruchomić metodę (jeśli jej wywołanie kończy się jakimś tam błędem) zanim poleci raport do admina. Advice typu around, joinPoint.proceed() w bloku try - catch i mamy sprawę załatwioną. Co więcej mamy ładne odseparowanie logiki metody biznesowej od logiki raportowania błędów.
- afterThrowing
- jeśli chcemy napisać generyczną obsługę wyjątków w aplikacji, logowanie wystąpienia wyjątku, można pisać obsługę wyjątku per typ itp. Pisałem kiedyś taki advice i w tamtym projekcie sprawował się świetnie.
- jeśli chcemy tłumaczyć wyjątki.
- w aplikacjach wielowarstwowych dobrze jest jeśli warstwa widzi tylko warstwę bezpośrednio pod nią. Jeśli mamy 3 warstwy : 1 -> 2 -> 3, to jeśli z dolnej warstwy (3) poleci wyjątek byłoby super gdyby warstwa 2 go przechwyciła i przetłumaczyła na wyjątek który rozumiałaby warstwa 1. Jeśli tego nie zrobimy (aspektem bądź inaczej) to 1 zaczyna widzieć bezpośrednio 3. Można się spierać czy to dobrze czy nie; wiem że spring korzysta z tłumaczenia wyjątków w kontekście warstw (np w module jdbc, o którym będę pisał).
- inny przypadek, który mi przychodzi do głowy to tłumaczenie wyjątków na styku serwer -> gui. Wydaje się fajnym pomysłem mieć jakiś mechanizm tłumaczący wyjątek z bebechów serwera na coś co użytkownik może zobaczyć w alercie.
Chciałbym jeszcze Czytelnikowi zwrócić uwagę, że do tej pory wszystkie moje advice-y były metodami publicznymi zwracającymi void. Nie ma znaczenia ich widoczność oraz co zwracają, z wyjątkiem advice typu around. Otóż w tym przypadku zwracany obiekt to jest obiekt zwracany przez metodę proceed() - czyli w konsekwencji na obiekcie target. Trzeba o tym pamiętać. Rzućmy okiem jeszcze raz na tą metodę:
@Around("stringParameter()") public Object around (ProceedingJoinPoint joinPoint) throws Throwable { logger.info("Around advice"); return joinPoint.proceed(); }
Tyle w temacie typów advice-ów w spring aop. Stąd można pobrać źródła projektu.
Brak komentarzy:
Prześlij komentarz