{"id":22851,"date":"2023-08-03T05:00:00","date_gmt":"2023-08-03T03:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=22851"},"modified":"2024-07-22T14:48:39","modified_gmt":"2024-07-22T12:48:39","slug":"spring-aop-programowanie-zorientowane-aspektowo-w-spring","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/spring-aop-programowanie-zorientowane-aspektowo-w-spring\/","title":{"rendered":"Spring AOP \u2013 programowanie zorientowane aspektowo w Spring"},"content":{"rendered":"\n<p>Cz\u0119sto przed programistami, opr\u00f3cz realizacji g\u0142\u00f3wnego zadania programistycznego, pojawia si\u0119 konieczno\u015b\u0107 implementacji dodatkowych wymaga\u0144 lub za\u0142o\u017ce\u0144 technicznych. Odpowiednim przyk\u0142adem obrazuj\u0105cym taki problem mo\u017ce by\u0107 konieczno\u015b\u0107 dodania do naszego kodu fragmentu odpowiedzialnego za obs\u0142ug\u0119 transakcji bazodanowej. W celu zachowania przejrzysto\u015bci logiki biznesowej, podeprze\u0107 mo\u017cemy si\u0119 programowaniem aspektowym.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Czym jest programowanie zorientowane aspektowo?<\/strong><\/h2>\n\n\n\n<p>Programowanie zorientowane aspektowo, AOP (ang. Aspect Oriented Programming) jest paradygmatem programowania, kt\u00f3ry zwi\u0119ksza modu\u0142owo\u015b\u0107, umo\u017cliwiaj\u0105c wydzielenie problem\u00f3w przekrojowych. Tak\u0105 mo\u017cliwo\u015b\u0107 uzyskujemy poprzez dodanie zachowania do istniej\u0105cego kodu bez jego modyfikacji (dodanie kodu w odr\u0119bnym miejscu i okre\u015blenie miejsca wykonania).<\/p>\n\n\n\n<p>Dzi\u0119ki temu mechanizmowi jeste\u015bmy w stanie doda\u0107 do programu zachowanie, kt\u00f3re nie jest kluczowe dla logiki biznesowej (przyk\u0142adowo: logowanie operacji, weryfikacj\u0119 uprawnie\u0144 czy zarz\u0105dzanie transakcjami), odseparowuj\u0105c ten kod od kodu bazowego, a dodatkowo m\u00f3c go wykorzystywa\u0107 globalnie dla wskazanych grup metod. Aspekty powinny realizowa\u0107 jedynie techniczne lub niefunkcjonalne wymagania.<\/p>\n\n\n\n<p>Programowanie aspektowe pozwala przerwa\u0107 wykonywanie metody i do\u0142\u0105czy\u0107 logik\u0119 przed, po lub w zale\u017cno\u015bci od wyst\u0105pienia specyficznego wydarzenia np. wyst\u0105pienia b\u0142\u0119du. Wa\u017cne, by metoda znajdowa\u0142a si\u0119 w beanie springowym.<\/p>\n\n\n\n<p>Wiele tego typu rozwi\u0105za\u0144 jest ju\u017c zaimplementowane (np. <strong>@Transactional<\/strong> czy <strong>@Timed<\/strong>), jednak na potrzeby artyku\u0142y zademonstrowane zostanie kilka przyk\u0142adowych implementacji podobnych problem\u00f3w.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Mechanizm dzia\u0142ania Proxy<\/strong><\/h2>\n\n\n\n<p>Spring AOP jest jedn\u0105 z g\u0142\u00f3wnych cz\u0119\u015bci frameworka Spring i odpowiada za realizacj\u0119 tematyki AOP. W Spring AOP istniej\u0105 dwa sposoby implementacji aspekt\u00f3w:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pierwszy z nich umo\u017cliwia implementacj\u0119 za pomoc\u0105 \u201eczystej\u201d klasy, kt\u00f3ra jest konfigurowana jako aspekt w springu za pomoc\u0105 konfiguracji XML,<\/li>\n\n\n\n<li>drugi spos\u00f3b umo\u017cliwia korzystanie z adnotacji pochodz\u0105cych z AspectJ. W p\u00f3\u017aniejszych przyk\u0142adach wykorzystany zostanie drugi spos\u00f3b.<\/li>\n<\/ul>\n\n\n\n<p>Aby zinterpretowa\u0107 dodatkow\u0105 logik\u0119 np. adnotacji, spring wykorzystuje mechanizm proxy (warstwy po\u015bredniej, kt\u00f3ra obudowuje wstrzykiwan\u0105 klas\u0119). Wykorzystywany do tego celu mo\u017ce by\u0107 zar\u00f3wno mechanizm proxy JDK jak i CGLIB (powszechna biblioteka definicji klas typu open source, zawarta w spring-core). Je\u015bli obiekt docelowy implementuje co najmniej jeden interfejs, u\u017cywamy proxy JDK. W przeciwnym przypadku wykorzystana zostanie biblioteka CGLIB.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/07\/Obraz1-1.png\"><img decoding=\"async\" width=\"480\" height=\"263\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/07\/Obraz1-1.png\" alt=\"Wizualizacja dzia\u0142ania mechanizmu proxy\" class=\"wp-image-22852\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/07\/Obraz1-1.png 480w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/07\/Obraz1-1-300x164.png 300w\" sizes=\"(max-width: 480px) 100vw, 480px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 1 Wizualizacja dzia\u0142ania mechanizmu proxy<\/figcaption><\/figure>\n\n\n\n<p>Szczeg\u00f3lnym, wartym uwagi przypadkiem jest fakt, \u017ce nawet poprawnie zaimplementowany aspekt nie zadzia\u0142a w przypadku wywo\u0142ania metody w obr\u0119bie jednej klasy (wywo\u0142anie metody x() z metody y() w obr\u0119bie tej samej klasy Z). Pomini\u0119te zostanie w ten spos\u00f3b wywo\u0142anie na klasy proxy, kt\u00f3ra odpowiada za dzia\u0142anie dodatkowych mechanizm\u00f3w. Mo\u017ce to rodzi\u0107 wiele potencjalnych problem\u00f3w oraz zachowanie aplikacji niezgodne z naszymi oczekiwaniami.<\/p>\n\n\n\n<p>Dodatkowo, warto pami\u0119ta\u0107, \u017ce aspekty nie zadzia\u0142aj\u0105 na metodach prywatnych (brak jest mo\u017cliwo\u015bci ich wywo\u0142ania spoza klasy, a wi\u0119c i opakowania w dodatkow\u0105 logik\u0119 w klasie proxy).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Om\u00f3wienie kluczowych element\u00f3w Spring AOP<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Aspect<\/strong><\/h3>\n\n\n\n<p>Jest modularyzacj\u0105 problemu, kt\u00f3ra mo\u017ce obejmowa\u0107 wiele klas. W Spring AOP <strong>@Aspect<\/strong> jest adnotacj\u0105, kt\u00f3r\u0105 opatrujemy klas\u0119 zawieraj\u0105c\u0105 definicj\u0119 naszych porad (w przypadku korzystania z konfiguracji przez adnotacj\u0119). Wspomniane adnotacje pochodz\u0105 z AspectJ i nie s\u0105 adnotacjami typowo Springowymi, natomiast Spring rozpoznaje je podczas uruchamiania aplikacji.<\/p>\n\n\n\n<p><strong>Uwaga<\/strong>: wymagane u\u017cycie dodatkowej adnotacji np. <strong>@Component<\/strong>, by klasa zosta\u0142a dodana do kontekstu.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Aspect\n@Component\npublic class ExampleAspect {\n\/\/ miejsce implementacji porad i punkt\u00f3w przeci\u0119cia\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\"><strong>Join point<\/strong><\/h3>\n\n\n\n<p>Punkt \u0142\u0105czenia jest specyficznym punktem podczas wykonywania programu w postaci wywo\u0142ania metody, zmiany stanu obiektu, zg\u0142oszenia wyj\u0105tku itd. Spring AOP obs\u0142uguje tylko join pointy w postaci wywo\u0142ania metod, co stanowi jedynie niewielk\u0105 cz\u0119\u015b\u0107 mo\u017cliwo\u015bci AspectJ. S\u0142owem kluczowym u\u017cywanym w Spring AOP do okre\u015blenia join pointu w postaci wywo\u0142ania metody jest \u201e<strong>execution\u201d<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Pointcut<\/strong><\/h3>\n\n\n\n<p>Precyzuje zestaw punkt\u00f3w przeci\u0119cia, w kt\u00f3rych nale\u017cy uruchomi\u0107 porad\u0119. W spring AOP istniej\u0105 dwa sposoby na okre\u015blenie takiego punktu:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>mo\u017cemy poda\u0107 jego definicj\u0119 bezpo\u015brednio jako argument adnotacji typu porady np. <strong>@Before<\/strong>,<\/li>\n\n\n\n<li>mo\u017cemy r\u00f3wnie\u017c zdefiniowa\u0107 go poprzez oznaczenie metody adnotacj\u0105 <strong>@JoinPoint<\/strong>, dzi\u0119ki czemu raz zdefiniowany punkt przeci\u0119cia mo\u017cna u\u017cy\u0107 podczas implementacji wielu porad, a nawet \u0142\u0105czy\u0107 je przy pomocy operator\u00f3w logicznych.<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Pointcut(&quot;execution(* pl.demo.spring.ExampleClass.exampleMethod(..))&quot;)\nvoid examplePointcutMethod() {\n\n}\n\n@Before(&quot;examplePointcutMethod()&quot;)\nvoid beforeSomeMethod(JoinPoint jp) {\n    logger.info(&quot;Call exampleMethod with arguments: {}&quot;, jp.getArgs());\n\n}\n<\/pre><\/div>\n\n\n<p>Powy\u017cszy kod mo\u017cna r\u00f3wnie\u017c zapisa\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Before(&quot;execution(* pl.demo.spring.ExampleClass.exampleMethod(..))&quot;)\nvoid beforeSomeMethod(JoinPoint jp) {\n    logger.info(&quot;Call exampleMethod with arguments: {}&quot;, jp.getArgs());\n\n}\n<\/pre><\/div>\n\n\n<p>Warto w tym miejscu przybli\u017cy\u0107 podstawowe mo\u017cliwo\u015bci konfiguracji punktu przeci\u0119cia. W celu poznania bardziej zaawansowanych technik zach\u0119cam do zapoznania si\u0119 <a href=\"https:\/\/docs.spring.io\/spring-framework\/reference\/\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >z dokumentacj\u0105 Spring AOP<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Execution<\/strong><\/h3>\n\n\n\n<p>Podstawowy desygnator umo\u017cliwiaj\u0105cy dopasowanie punkt\u00f3w \u0142\u0105czenia wykonania metody. Mo\u017cemy w nim okre\u015bli\u0107 kolejno:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>modyfikator dost\u0119pu (opcjonalnie),<\/li>\n\n\n\n<li>zwracany typ,<\/li>\n\n\n\n<li>pakiet,<\/li>\n\n\n\n<li>klas\u0119,<\/li>\n\n\n\n<li>parametry.<\/li>\n<\/ul>\n\n\n\n<p>Dodatkowo mo\u017cemy u\u017cywa\u0107 symbolu wieloznaczno\u015bci \u201e*\u201d. Dowoln\u0105 liczb\u0119 argument\u00f3w oraz ich typ, oznaczamy symbolem dw\u00f3ch kropek \u201e..\u201d.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Annotation<\/strong><\/h3>\n\n\n\n<p>Przyk\u0142adem r\u00f3wnie popularnego desygnatora jest desygnator @annotation. Pozwala on ograniczy\u0107 dopasowanie metod oznaczonych specjaln\u0105 adnotacj\u0105. Dodaj\u0105c do tego mo\u017cliwo\u015b\u0107 tworzenia w\u0142asnych adnotacji, zyskujemy \u0142atwy spos\u00f3b na uruchomienie porad dla wskazanych metod. Przyk\u0142adem zastosowania mo\u017ce by\u0107 oznaczenie metod, dla wywo\u0142a\u0144 kt\u00f3rych chcemy mierzy\u0107 czas wykonania \u2013 taki te\u017c przyk\u0142ad zaprezentowano w dalszej cz\u0119\u015bci artyku\u0142u.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Advice<\/strong><\/h3>\n\n\n\n<p>Porada jest dzia\u0142aniem podj\u0119tym dla okre\u015blonego punktu \u0142\u0105czenia (ang. Join point). Dla programisty s\u0105 to metody wykonane w aplikacji w przypadku, gdy zostanie osi\u0105gni\u0119ty okre\u015blony punkt \u0142\u0105czenia z pasuj\u0105cym punktem przeci\u0119cia (ang. Point cut). Poni\u017cej przedstawi\u0142em rodzaje dost\u0119pnych porad wraz z kr\u00f3tkim opisem ich przeznaczenia.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Typy porad<\/strong><\/h4>\n\n\n\n<p><strong>Before advice<\/strong> \u2013 porada, kt\u00f3ra zostanie wykonana przed metod\u0105 punktu \u0142\u0105czenia. Adnotacj\u0105 s\u0142u\u017c\u0105c\u0105 do oznaczenia takiej metody jest adnotacja <strong>@Before<\/strong> z pakietu <strong>org.aspectj.lang.annotation<\/strong>. Porada ta jako parametr mo\u017ce przyjmowa\u0107 obiekt typu JoinPoint. Nie jest on jednak obowi\u0105zkowy.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Before(&quot;pointcut()&quot;)\npublic void beforeMethod(JoinPoint joinPoint)\n{\n\/\/...\n}\n<\/pre><\/div>\n\n\n<p><strong>After advice<\/strong> \u2013 porada, kt\u00f3ra zostanie zrealizowana po wykonaniu metod punktu \u0142\u0105czenia. Nale\u017cy zwr\u00f3ci\u0107 uwag\u0119, i\u017c ten typ porady wykona si\u0119 niezale\u017cnie od wyniku metody (zar\u00f3wno w przypadku pomy\u015blnego wywo\u0142ania jak i w przypadku wyst\u0105pienia wyj\u0105tku). Jej dzia\u0142anie mo\u017cna por\u00f3wna\u0107 do bloku finally. Adnotacja, jak\u0105 nale\u017cy j\u0105 oznaczy\u0107, to <strong>@After<\/strong>. Porada ta jako parametr mo\u017ce przyjmowa\u0107 obiekt typu JoinPoint.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@After(&quot;pointcut()&quot;)\npublic void afterMethod(JoinPoint joinPoint)\n{\n\/\/...\n}\n<\/pre><\/div>\n\n\n<p><strong>After returning<\/strong> \u2013 w przeciwie\u0144stwie do porad typu @After mo\u017cemy u\u017cy\u0107 porady, kt\u00f3ra wykonana zostanie tylko w przypadku pomy\u015blnego wywo\u0142ania metody. Tak\u0105 porad\u0119 nale\u017cy oznaczy\u0107 adnotacj\u0105 <strong>@AfterReturning<\/strong>. Jako parametr mo\u017ce przyjmowa\u0107 ona obiekt typu JoinPoint oraz obiekt zwracanej przez funkcj\u0119 warto\u015bci (powi\u0105zanie nast\u0119puje za pomoc\u0105 parametru returning adnotacji).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@AfterReturning(pointcut = &quot;pointcut()&quot;, returning = &quot;response&quot;)\npublic void afterReturningMethod(JoinPoint joinPoint, ResponseEntity&amp;lt;?&gt; response)\n{\n\/\/...\n}\n<\/pre><\/div>\n\n\n<p><strong>After throwing<\/strong> \u2013 uzupe\u0142nienie stanowi porada, kt\u00f3ra wykona si\u0119 tylko w przypadku, gdy metoda punktu \u0142\u0105czenia zg\u0142osi wyj\u0105tek. Tego typu porady nale\u017cy odznaczy\u0107 adnotacj\u0105 <strong>@AfterThrowing<\/strong>. Jako parametr mo\u017ce ona przyjmowa\u0107 obiektu typu JoinPoint oraz obiekt zg\u0142aszanego wyj\u0105tku (powi\u0105zany za pomoc\u0105 atrybutu throwing adnotacji).&nbsp;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@AfterThrowing(pointcut = &quot;pointcut()&quot;, throwing = &quot;ex&quot;)\npublic void afterThrowingMethod(JoinPoint joinPoint, Exception ex) {\n\/\/...\n}\n<\/pre><\/div>\n\n\n<p><strong>Around<\/strong> \u2013 porada, kt\u00f3ra pozwala nam na otoczenie wywo\u0142ania metody punktu przeci\u0119cia oraz dodanie logiki zar\u00f3wno przed jej wykonaniem jak i po. Dodatkowo, sami jeste\u015bmy odpowiedzialni za jej wykonanie i dzi\u0119ki niej mamy mo\u017cliwo\u015b\u0107 pomini\u0119cia wykonania takiej metody. Do tworzenia tego typu porady wykorzystywana jest adnotacja <strong>@Around<\/strong>.<\/p>\n\n\n\n<p>W przeciwie\u0144stwie do poprzednich typ\u00f3w, porada ta przyjmuje obiekt typu ProceedingJoinPoint, na kt\u00f3rym mamy mo\u017cliwo\u015b\u0107 wywo\u0142a\u0107 metod\u0119 proceed(), w celu wykonania metody docelowej. Dodatkowo nale\u017cy zadba\u0107 o zwr\u00f3cenie rezultatu wykonanej metody.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Around(&quot;pointcut()&quot;)\npublic Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {\n\/\/...\n    Object result = proceedingJoinPoint.proceed();\n\/\/...\n    return result;\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>Dost\u0119p do bie\u017c\u0105cego punktu \u0142\u0105czenia<\/strong><\/h2>\n\n\n\n<p>Wszystkie porady maj\u0105 mo\u017cliwo\u015b\u0107 zadeklarowania jako pierwszego parametru obiektu typu JoinPoint. Wyj\u0105tek stanowi porada Around, kt\u00f3ra wymaga zadeklarowania jako pierwszego parametru obiektu typu ProceedingJoinPoint, kt\u00f3ry rozszerza interfejs JoinPoint.<\/p>\n\n\n\n<p>Parametr JoinPoint w poradzie zapewnia wiele metod, kt\u00f3re mog\u0105 wzbogaci\u0107 implementacj\u0119 naszej porady. Dzi\u0119ki niemu mo\u017cemy na przyk\u0142ad uzyska\u0107 dost\u0119p do sygnatury wywo\u0142ywanej metody JoinPoint::getSignature() oraz jej parametr\u00f3w JoinPoint::getArgs(). ProceedingJoinPoint pozwala dodatkowo sterowa\u0107 wywo\u0142aniem metody ProceedingJoinPoint::proceed().<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Dodanie i konfiguracja zale\u017cno\u015bci Spring AOP w projekcie<\/strong><\/h2>\n\n\n\n<p>Pierwsz\u0105 czynno\u015bci\u0105, jak\u0105 nale\u017cy wykona\u0107, aby rozpocz\u0105\u0107 przygod\u0119 z programowaniem aspektowym w frameworku SpringBoot, jest dodanie zale\u017cno\u015bci <strong>spring-boot-starter-aop<\/strong> w pliku konfiguracyjnym projektu (build.gradle dla projektu zarz\u0105dzanego przez gradle lub pom.xml w przypadku projektu zarz\u0105dzanego przez maven).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&amp;lt;dependencies&gt;\n   &amp;lt;dependency&gt;\n      &amp;lt;groupId&gt;org.springframework.boot&amp;lt;\/groupId&gt;\n      &amp;lt;artifactId&gt;spring-boot-starter-aop&amp;lt;\/artifactId&gt;\n   &amp;lt;\/dependency&gt;\n   &amp;lt;!--   kolejne zale\u017cno\u015bci    --&gt;\n&amp;lt;\/dependencies&gt;\n<\/pre><\/div>\n\n\n<p>Nast\u0119pnie, w klasie konfiguracyjnej, przy u\u017cyciu adnotacji <strong>@EnableAspectJAutoProxy<\/strong>, nale\u017cy odblokowa\u0107 wsparcie dla komponent\u00f3w oznaczonych adnotacj\u0105 <strong>@Aspect<\/strong>. Wspomnian\u0105 adnotacj\u0119 mo\u017cna doda\u0107 w g\u0142\u00f3wnym pliku konfiguracji aplikacji lub w dowolnym komponencie konfiguracyjnym:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@SpringBootApplication\n@EnableAspectJAutoProxy\npublic class AspectApplication {\n\n   public static void main(String&#x5B;] args) {\n      SpringApplication.run(AspectApplication.class, args);\n   }\n\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>Implementacje przyk\u0142adowych problem\u00f3w<\/strong><\/h2>\n\n\n\n<p>Na potrzeby artyku\u0142u zaprezentowane zostan\u0105 3 przyk\u0142adowe implementacje aspekt\u00f3w.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pierwsza z nich dotyczy\u0107 b\u0119dzie <strong>porady typu Before<\/strong>, gdzie dodana zostanie implementacja loguj\u0105ca wywo\u0142anie endpoint\u00f3w w przyk\u0142adowym kontrolerze.<\/li>\n\n\n\n<li>Nast\u0119pnie przedstawiona i om\u00f3wiona zostanie implementacja aspektu typu <strong>@Around<\/strong> na przyk\u0142adzie logowania czasu trwania wybranych operacji, dodatkowo wykorzystany zostanie desygnator <strong>@annotation<\/strong>.<\/li>\n\n\n\n<li>Ostatni przyk\u0142ad przedstawia\u0107 b\u0119dzie logowanie sytuacji nadzwyczajnych, gdzie zwr\u00f3cony zosta\u0142 nieobs\u0142u\u017cony wyj\u0105tek. Logowanie przy pomocy aspekt\u00f3w nie jest ich jedynym przeznaczeniem. Jednak na potrzeby prezentacji poszczeg\u00f3lnych typ\u00f3w aspekt\u00f3w, jest w zupe\u0142no\u015bci wystarczaj\u0105ce i w ca\u0142o\u015bci pozwoli skupi\u0107 si\u0119 na omawianej tematyce.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Parametry<\/strong><\/h3>\n\n\n\n<p>Poni\u017cej przedstawiony zostanie kod kontrolera, serwisu, modelu oraz adnotacji, na podstawie kt\u00f3rych stworzone zostan\u0105 aspekty.<\/p>\n\n\n\n<p><strong>TaxController<\/strong> \u2013 zawiera implementacj\u0119 przyk\u0142adowego kontrolera restowego. Metoda calculateTax zosta\u0142a oznaczona specjalnie stworzon\u0105 adnotacj\u0105 @LogExecutionTime, kt\u00f3ra pos\u0142u\u017cy do prezentacji tworzenia pointcutu z u\u017cyciem desygnatora @annotation.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@RestController\n@RequestMapping(&quot;\/tax&quot;)\npublic class TaxController {\n\n    private final TaxService taxService;\n\n    public TaxController(TaxService taxService) {\n        this.taxService = taxService;\n    }\n\n    @GetMapping(&quot;\/calculate\/{clientId}&quot;)\n    @LogExecutionTime\n    ResponseEntity&amp;lt;TaxResult&gt; calculateTax(@PathVariable Long clientId) {\n        TaxResult result = taxService.calculateTax(clientId);\n        return ResponseEntity.ok(result);\n    }\n\n}\n<\/pre><\/div>\n\n\n<p><strong>TaxService<\/strong> \u2013 kod przyk\u0142adowej implementacji serwisu, kt\u00f3ry w zale\u017cno\u015bci od przekazanego parametru clientId pozwoli zasymulowa\u0107 3 rodzaje operacji (wywo\u0142anie operacji, operacj\u0119 trwaj\u0105ca d\u0142u\u017cej oraz operacj\u0119 zako\u0144czon\u0105 zg\u0142oszeniem wyj\u0105tku). Tak przygotowana metoda pozwoli w pe\u0142ni przetestowa\u0107 wszystkie stworzone porady.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Service\npublic class TaxService {\n\n    TaxResult calculateTax(Long clientId) {\n        if (clientId == 2) {\n            throw new IllegalStateException(&quot;Calculating for this client is not allowed!&quot;);\n        }\n        if (clientId == 3) {\n            sleepFor5Seconds();\n        }\n        return new TaxResult(clientId);\n    }\n\n    private void sleepFor5Seconds() {\n        try {\n            Thread.sleep(5000L);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n    }\n\n}\n<\/pre><\/div>\n\n\n<p><strong>TaxResult<\/strong> \u2013 prosta klasa modelu wykorzystywanego w przyk\u0142adzie.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\npublic class TaxResult {\n\n    private Long clientId;\n\n    private BigDecimal tax;\n\n    public TaxResult(Long clientId) {\n        this.clientId = clientId;\n    }\n\n    public TaxResult(Long clientId, BigDecimal tax) {\n        this.clientId = clientId;\n        this.tax = tax;\n    }\n\n    public Long getClientId() {\n        return clientId;\n    }\n\n    public void setClientId(Long clientId) {\n        this.clientId = clientId;\n    }\n\n    public BigDecimal getTax() {\n        return tax;\n    }\n\n    public void setTax(BigDecimal tax) {\n        this.tax = tax;\n    }\n}\n<\/pre><\/div>\n\n\n<p><strong>LogExecutionTime<\/strong> \u2013 kod stworzonej adnotacji, kt\u00f3r\u0105 oznaczono metod\u0119 calculateTax z klasy TaxController.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface LogExecutionTime {\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>Implementacja przyk\u0142adowych porad<\/strong><\/h2>\n\n\n\n<p>Porada typu @Before przedstawia logowanie wywo\u0142ania ka\u017cdej metody z klasy TaxController. W tym przypadku zaprezentowano przekazywanie definicji pointcutu jako nazwy zdefiniowanej metody. Wykorzystano r\u00f3wnie\u017c pobranie nazwy metody, dla kt\u00f3rej uruchomiona zosta\u0142a porada za po\u015brednictwem metody JoinPoint::getSignature()::getName() oraz przekazanych argument\u00f3w JoinPoint::getArgs().<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Aspect\n@Component\npublic class TaxAspectBefore {\n\n    public static final Logger logger = LoggerFactory.getLogger(TaxAspectBefore.class);\n\n    @Pointcut(&quot;execution(* pl.demo.spring.TaxController.*(..))&quot;)\n    void anyMethodOfTaxController() {\n\n    }\n\n    @Before(&quot;anyMethodOfTaxController()&quot;)\n    void logCallTaxEndpoint(JoinPoint joinPoint) {\n        logger.info(&quot;Call TaxController endpoint: {} with arguments {}&quot;, joinPoint.getSignature().getName(), joinPoint.getArgs());\n    }\n\n}\n<\/pre><\/div>\n\n\n<p>Przy pomocy porady typu @Around zaimplementowane zosta\u0142o logowanie czasu wykonania metody. Metoda ta zosta\u0142a opatrzona adnotacj\u0105 @LogExecutionTime. Wykorzystany do tego celu zosta\u0142 desygnator typu @annotation, kt\u00f3ry jako parametr przyjmuje nazw\u0119 adnotacji wraz z pakietem. Dodatkowo, warto zwr\u00f3ci\u0107 uwag\u0119 na konieczno\u015b\u0107 wykonania metody proceedingJoinPoint.proceed() oraz przypisania do zmiennej i zwr\u00f3cenia jej wyniku.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Aspect\n@Component\npublic class TaxAspectAround {\n\n    public static final Logger logger = LoggerFactory.getLogger(TaxAspectAround.class);\n\n    @Around(&quot;@annotation(pl.demo.spring.LogExecutionTime)&quot;)\n    Object logExecutionTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {\n        long start = System.currentTimeMillis();\n        Object result = proceedingJoinPoint.proceed();\n        long duration = (System.currentTimeMillis() - start) \/ 1000;\n        logger.info(&quot;Execution of method {} took {} s&quot;, proceedingJoinPoint.getSignature().getName(), duration);\n        return result;\n    }\n\n}\n<\/pre><\/div>\n\n\n<p>Jako ostatnia przedstawiona zosta\u0142a implementacja dodatkowego logowania w przypadku zg\u0142oszenia wyj\u0105tku przez metod\u0119 calculateTax z serwisu TaxService. Za pomoc\u0105 atrybutu throwing adnotacji @AfterThrowing okre\u015blamy mapowanie zg\u0142oszonego wyj\u0105tku do parametru metody. W logowaniu wykorzystano wcze\u015bniej poznane metody z klasy JoinPoint, a dodatkowo informacje pochodz\u0105ce ze zg\u0142oszonego wyj\u0105tku.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Aspect\n@Component\npublic class TaxAspectAfterThrowing {\n\n    public static final Logger logger = LoggerFactory.getLogger(TaxAspectAfterThrowing.class);\n\n    @AfterThrowing(pointcut = &quot;execution(TaxResult pl.demo.spring.TaxService.calculateTax(Long))&quot;, throwing = &quot;exception&quot;)\n    void logAfterThrowingInCalculateTaxMethod(JoinPoint joinPoint, Exception exception) {\n        logger.error(&quot;TaxService::calculateTax throw error for clientId: {} with message: {}&quot;, joinPoint.getArgs()&#x5B;0], exception.getMessage());\n    }\n\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>Podsumowanie<\/strong><\/h2>\n\n\n\n<p>Wykorzystanie podej\u015bcia zorientowanego na aspekty w tworzonej aplikacji mo\u017ce stanowi\u0107 doskona\u0142e rozwi\u0105zanie do oddzielenia niefunkcjonalnych\/technicznych fragment\u00f3w kodu od logiki biznesowej.<\/p>\n\n\n\n<p>Nale\u017cy jednak mie\u0107 na uwadze, i\u017c dodanie kolejnego poziomu abstrakcji mo\u017ce stanowi\u0107 problem dla mniej do\u015bwiadczonych programist\u00f3w oraz utrudnia\u0107 proces debugowania. Dlatego <strong>warto korzysta\u0107 z nich w spos\u00f3b przemy\u015blany<\/strong> oraz umieszcza\u0107 je w jednym miejscu, w celu szybkiej ich identyfikacji.<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;22851&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;3&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 3)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Spring AOP \u2013 programowanie zorientowane aspektowo w Spring&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 3)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Cz\u0119sto przed programistami, opr\u00f3cz realizacji g\u0142\u00f3wnego zadania programistycznego, pojawia si\u0119 konieczno\u015b\u0107 implementacji dodatkowych wymaga\u0144 lub za\u0142o\u017ce\u0144 technicznych. Odpowiednim przyk\u0142adem obrazuj\u0105cym &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/spring-aop-programowanie-zorientowane-aspektowo-w-spring\/\">Continued<\/a><\/p>\n","protected":false},"author":544,"featured_media":22858,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[2427,1756,1755,1033],"class_list":["post-22851","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-digital","tag-aspect-oriented-programming","tag-spring-aop","tag-spring"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/07\/Spring-AOP-\u2013-programowanie-zorientowane-aspektowo-w-Spring.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/22851"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/544"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=22851"}],"version-history":[{"count":3,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/22851\/revisions"}],"predecessor-version":[{"id":22860,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/22851\/revisions\/22860"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/22858"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=22851"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=22851"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=22851"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}