اول از همه بگم که اطلاعاتی که توی این آموزش هست از دو منبع خوب گرفتم :
1- یه ویدیو آموزشی خیلی خوب از JavaLand که لینکش رو این پایین میزارم حتما ببینید:
https://www.aparat.com/v/lhEjc
2- وبسایت geeksforgeeks : این سایت برای ما بستست اگر باز نشد فیلتر شکن بزنید
https://www.geeksforgeeks.org/spring-boot-difference-between-aop-and-aspectj/
https://www.geeksforgeeks.org/spring-boot-aop-after-throwing-advice/
در AOP از مفهوم Aspect به جای کلاس استفاده میشود.
این مفهوم باعث تفکیک شدن منطق و کد های برنامه به بخش های مجزایی می شود. مثلا : Security – logging و ...
توابعی که بخش های مختلف برنامه را به هم وصل میکند Cross-cutting-concerns میگویند.
میتوان منطق کد را از دیگر بخش های کد تفکیک کنیم .
به جای اینکه یک بخش از کد را که مربوط به بخش های فنی برنامه است مثل Logging، Transaction و Security به جای اینکه داخل بدنه کد پیاده سازی کنیم . داخل یک کلاس دیگه که اینجا ما بهش یک Aspect میگیم پیاده سازی کنیم. و این Aspect را Point کنیم به متدهایی که میخواهیم اعمال شود. و وادارش کنیم که قبل و بعد از یک متد یا یک اتفاقی که میفته یک قطعه کد قبل یا بعدش اجرا شود.
1- حجم کد کم بشه
2- خوانایی کد بیشتر بشه
3- تمرکز بیشتری روی Logic برنامه داشته باشیم.
Spring AOP رهگیری های کدی در اختیار شما قرار میدهد که میتوانید فرآیند اجرای Application را قطع کنید.
برای مثال :
· وقتی که یک متد اجرا می شود. شما میتوانید کاربردهایی را برای مراحل قبل و بعد از اجرای متد به آن اضافه کنید.
· قبل از اینکه این متد کال بشود بیا توی DB یه لاگ بزن وقتی هم تمام شد لاگ بزن با موفقیت انجام شد.
· حتی میتوانید بفهمید خروجی متد چی شد.
· متد Exception دارد یا خیر . و بر اساس اون بیایم تصمیماتی بگیریم .
حالا Spring پشت صحنه دوتا روش برای پیاده سازی AOP ارائه میدهد :
1- AspectJ
2- Spring AOP
به ماژول های مجزای مستقل ، Aspect گفته میشود. به عنوان مثال Aspect ورود به سیستم یا لاگ زدن و ...
یا به عبارتی دیگر ماژولی است که دارای تعدادی API بوده و در زمینه های لازم جهت Cross-Cutting را فراهم میکند.
این نقطه ای است که می توان Aspect هایی را برای انجام یک کار خاص تزریق کرد. بلوک کد واقعی است که قبل یا بعد از اجرای متد اجرا می شود.
نقاطی که قصد داریم Aspect روی آن ها اجرا شود مثل Method ، Field ، Constructor و ...
در واقع نقطه ای از برنامه شما است که از طریق آن میتوان AOP Aspect را به برنامه متصل کنید.
مجموعه ای از یک یا چند نقطه اتصال است که کد واقعی در آنجا اجرا می شود. PointCut ها با استفاده از عبارت یا الگوها مشخص می شوند.
مجموعه ای از یک یا چند JoinPoint است که Advice بایستی در آن اجرا گردد.
یا به عبارتی به جستجویی (Search) میگویند که ما مینویسیم تا بگوییم روی چه متد و یا از روی چه کلاس هایی قصد داریم این Aspect اتفاق بیفتد.
شیء است که توسط یک یا چند Aspect مورد توجه قرار گرفته و همیشه یک Proxy خواهد بود . همچنین به آن Advice Object هم میگویند.
یا به عبارتی میتوان گفت همان شیء که Aspect روی آن اتفاق میفتد.
کدی که مینویسیم رو بهش میگن Advice
@Aspect @Component public class LoggingAspectPointCutExample{ @PointCut("execution(*java11.fundamentals.*.*(...))") private void logData() {} }
در PointCut میگوییم که ما فقط میخواهیم روی این JoinPoint این Aspect اجرا شود.
وقتی ما یک Method را صدا میزنیم . در حقیقت Method ما صدا زده نمیشه و در واقع میره و اون Aspect را اجرا میکند. بعد Aspect میاد اقداماتی که لازم دارد را روی اون اعمال میکند . و سپس Aspect متد ما را صدا میزند.
و بعد از صدا زده شدن متد ما میتواند اقداماتی انجام دهیم.
Before : این Advice قبل از اجرا شدن متد مورد نظر اجرا خواهد شد.
After : این Advice بعد از اجرا شدن متد مورد نظر اجرا خواهد شد. و بدون توجه به خروجی
After-Running : پس از اجرای متد ، به شرطی که کاملا به درستی اجرا شده باشد.
After-Throwing : پس از اجرای متد ، به شرطی که در آن متد Exception پرتاب شده باشد.
Around : قبل و بعد از فراخوانی متد مورد نظر اجرا می گردد.
JDK dynamic proxy
CGLIB proxy
1- فیلد
2- متد
3- Constructor
4- هر کلاسی که در Class Loader باشد را هدف قرار میدهد (Entity ها و ...)
1- در زمان کامپایل
2- پس از کامپایل
3- در زمان بارگذاری.
خوب دیگه فکر کنم خیلی حرف زدیم . گفتنی ها رو گفتیم . از همه چیز که بگذریم سخن دوست خوش تر است (اشاره به ساحت مقدس جاوا):
اول از همه dependency زیر رو در POM فایل خودتون قرار بدید :
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
حالا بیایم یه کلاس بنویسیم که در واقع یه Rest Controller خیلی سادست:
package ir.sadegh.aop_demo.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/aop/demo") public class MainController { @GetMapping("/get/aop") public StringsayHello(){ return "Hello" } }
حالا بریم برای نوشتن Aspect که یه لاگ میزنه:
package ir.sadegh.aop_demo.AOP; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class LogAspect { private static Log LOGGER = LogFactory.getLog(LogAspect.class.getName()); @Pointcut("execution(public * ir.sadegh.aop_demo.Controller.*.*(..))") public void allMethods(){ } @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)") public void logWithAnnotation(){ } @Around("allMethods() && logWithAnnotation()") public Object LogAround(ProceedingJoinPoint joinPoint) throws Throwable{ LOGGER.info(String.format( "Method %s is going to call from class %s" , joinPoint.getSignature().toString(), joinPoint.getThis().getClass().getSimpleName() )); Object returnObj = joinPoint.proceed(joinPoint.getArgs()); LOGGER.info(String.format( "Method %s has called from class %s with return value type %s" , joinPoint.getSignature().toString(), joinPoint.getThis().getClass().getSimpleName(), returnObj.getClass().getName() )); return returnObj; } }
خوب دوستان این آموزش هم به پایان رسید اگر جایی مشکل داشت حتما بهم بگید تا رفع کنم .
تمام تلاشم رو کردم گفتنی هارو گفته باشم.
شاد و موفق و پیروز باشید.