صادق خانزادی
صادق خانزادی
خواندن ۶ دقیقه·۲ سال پیش

Spring AOP ( Aspect Oriented Programming)

Spring AOP Baner
Spring AOP Baner

اول از همه بگم که اطلاعاتی که توی این آموزش هست از دو منبع خوب گرفتم :

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/



Aspect Oriented Programming

در 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 گفته میشود. به عنوان مثال Aspect ورود به سیستم یا لاگ زدن و ...

یا به عبارتی دیگر ماژولی است که دارای تعدادی API بوده و در زمینه های لازم جهت Cross-Cutting را فراهم میکند.

Join Points:

این نقطه ای است که می توان Aspect هایی را برای انجام یک کار خاص تزریق کرد. بلوک کد واقعی است که قبل یا بعد از اجرای متد اجرا می شود.

نقاطی که قصد داریم Aspect روی آن ها اجرا شود مثل Method ، Field ، Constructor و ...

در واقع نقطه ای از برنامه شما است که از طریق آن میتوان AOP Aspect را به برنامه متصل کنید.

Pointcut:

مجموعه ای از یک یا چند نقطه اتصال است که کد واقعی در آنجا اجرا می شود. PointCut ها با استفاده از عبارت یا الگوها مشخص می شوند.

مجموعه ای از یک یا چند JoinPoint است که Advice بایستی در آن اجرا گردد.

یا به عبارتی به جستجویی (Search) میگویند که ما مینویسیم تا بگوییم روی چه متد و یا از روی چه کلاس هایی قصد داریم این Aspect اتفاق بیفتد.

Target Object :

شیء است که توسط یک یا چند Aspect مورد توجه قرار گرفته و همیشه یک Proxy خواهد بود . همچنین به آن Advice Object هم میگویند.

یا به عبارتی میتوان گفت همان شیء که Aspect روی آن اتفاق میفتد.

Advice :

کدی که مینویسیم رو بهش میگن Advice

@Aspect @Component public class LoggingAspectPointCutExample{ @PointCut(&quotexecution(*java11.fundamentals.*.*(...))&quot) private void logData() {} }

نکته :

در PointCut میگوییم که ما فقط میخواهیم روی این JoinPoint این Aspect اجرا شود.

وقتی ما یک Method را صدا میزنیم . در حقیقت Method ما صدا زده نمیشه و در واقع میره و اون Aspect را اجرا میکند. بعد Aspect میاد اقداماتی که لازم دارد را روی اون اعمال میکند . و سپس Aspect متد ما را صدا میزند.

و بعد از صدا زده شدن متد ما میتواند اقداماتی انجام دهیم.

Advice Image
Advice Image



مدل های مختلف Advice

کجا ها میتوان Advice ها را قرار داد:

Before : این Advice قبل از اجرا شدن متد مورد نظر اجرا خواهد شد.

After : این Advice بعد از اجرا شدن متد مورد نظر اجرا خواهد شد. و بدون توجه به خروجی

After-Running : پس از اجرای متد ، به شرطی که کاملا به درستی اجرا شده باشد.

After-Throwing : پس از اجرای متد ، به شرطی که در آن متد Exception پرتاب شده باشد.

Around : قبل و بعد از فراخوانی متد مورد نظر اجرا می گردد.



تفاوت بین AOP و AspectJ

Spring AOP :

  • فقط روی متد ها اجرا میگردد . (فقط روی بین ها جواب میدهد)
  • تمام امکانات AOP را در اختیار ما قرار نمیدهد - آن را فقط می توان برای Bean هایی اعمال کرد که توسط یک Spring Container مدیریت می شوند. در واقع فقط بر روی متد ها کار میکند.
  • در زمان Runtime اجرا میشود.
  • این یک چارچوب AOP مبتنی بر پروکسی است.
  • این بدان معنی است که برای پیاده سازی Aspect ها در Object های مورد نظر ، پروکسی هایی از آن شی ایجاد می کند. این با استفاده از یکی از دو روش زیر به دست می آید:

JDK dynamic proxy

CGLIB proxy

Spring AOP Process Image
Spring AOP Process Image
  • خیلی از AspectJ کند تر است.
  • خیلی ساده میتوان یادش گرفت و ازش استفاده کرد.

AspectJ :

  • غیر از متد هم میتواند اجرا شود.

1- فیلد

2- متد

3- Constructor

4- هر کلاسی که در Class Loader باشد را هدف قرار میدهد (Entity ها و ...)

  • زمانی که نیاز به Advice Object هایی داشته باشیم که توسط Spring Container مدیریت نمیشوند AspectJ بهترین گزینه است.
  • همچنین اگر زمانی نیاز به اضافه کردن PointCut ها روی Field ها ، Constructor ها و ... داشته باشیم (نه فقط روی متد ها) از این قابلیت استفاده میکنیم .
  • AspectJ فناوری اصلی AOP است که هدف آن ارائه یک راه حل کامل AOP است.
  • از سه نوع بافت مختلف استفاده می کند:

1- در زمان کامپایل

2- پس از کامپایل

3- در زمان بارگذاری.

  • از طرف دیگر، این در زمان اجرا هیچ کاری انجام نمی دهد زیرا هر کلاس مستقیماً در زمان کامپایل با Aspect مربوط به خودش کامپایل میشه.
  • و بنابراین برخلاف Spring AOP، به هیچ الگوی طراحی نیاز ندارد.
  • عملکرد بهتری نسبت به Spring AOP دارد.
  • خیلی پیچیده تر از Spring AOP میباشد.




خوب دیگه فکر کنم خیلی حرف زدیم . گفتنی ها رو گفتیم . از همه چیز که بگذریم سخن دوست خوش تر است (اشاره به ساحت مقدس جاوا):

اول از همه 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(&quot/aop/demo&quot) public class MainController { @GetMapping(&quot/get/aop&quot) public StringsayHello(){ return &quotHello&quot } }

حالا بریم برای نوشتن 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(&quotexecution(public * ir.sadegh.aop_demo.Controller.*.*(..))&quot) public void allMethods(){ } @Pointcut(&quot@annotation(org.springframework.web.bind.annotation.GetMapping)&quot) public void logWithAnnotation(){ } @Around(&quotallMethods() && logWithAnnotation()&quot) public Object LogAround(ProceedingJoinPoint joinPoint) throws Throwable{ LOGGER.info(String.format( &quotMethod %s is going to call from class %s&quot , joinPoint.getSignature().toString(), joinPoint.getThis().getClass().getSimpleName() )); Object returnObj = joinPoint.proceed(joinPoint.getArgs()); LOGGER.info(String.format( &quotMethod %s has called from class %s with return value type %s&quot , joinPoint.getSignature().toString(), joinPoint.getThis().getClass().getSimpleName(), returnObj.getClass().getName() )); return returnObj; } }

خوب دوستان این آموزش هم به پایان رسید اگر جایی مشکل داشت حتما بهم بگید تا رفع کنم .

تمام تلاشم رو کردم گفتنی هارو گفته باشم.

شاد و موفق و پیروز باشید.

aop
Java Developer - Technical Team Lead At Dotin
شاید از این پست‌ها خوشتان بیاید