CodeGym /وبلاگ جاوا /Random-FA /AOP چیست؟ اصول برنامه نویسی جنبه گرا
John Squirrels
مرحله
San Francisco

AOP چیست؟ اصول برنامه نویسی جنبه گرا

در گروه منتشر شد
سلام، بچه ها و دختران! بدون درک مفاهیم اساسی، بررسی چارچوب ها و رویکردهای عملکرد ساخت بسیار دشوار است. بنابراین امروز ما در مورد یکی از این مفاهیم صحبت خواهیم کرد - AOP، یا برنامه نویسی جنبه گرا . AOP چیست؟  اصول برنامه نویسی جنبه گرا - 1این موضوع آسان نیست و به ندرت به طور مستقیم استفاده می شود، اما بسیاری از فریمورک ها و فناوری ها از آن زیر پوشش استفاده می کنند. و البته، گاهی اوقات در طول مصاحبه، ممکن است از شما خواسته شود که به طور کلی توضیح دهید که این چه نوع جانوری است و در کجا می توان از آن استفاده کرد. بنابراین بیایید نگاهی به مفاهیم اولیه و چند مثال ساده از AOP در جاوا بیاندازیم . اکنون، AOP مخفف برنامه‌نویسی جنبه‌محور است، که پارادایم‌ای است که برای افزایش مدولار بودن بخش‌های مختلف یک برنامه با جداسازی نگرانی‌های مقطعی در نظر گرفته شده است. برای انجام این کار، بدون ایجاد تغییرات در کد اصلی، رفتار اضافی به کد موجود اضافه می شود. به عبارت دیگر، می‌توانیم آن را به عنوان آویزان کردن عملکرد اضافی در بالای متدها و کلاس‌ها بدون تغییر در کد اصلاح‌شده در نظر بگیریم. چرا این لازم است؟ دیر یا زود، نتیجه می گیریم که رویکرد شی گرا معمولی همیشه نمی تواند به طور موثر مشکلات خاصی را حل کند. و هنگامی که آن لحظه فرا می رسد، AOP به کمک می آید و ابزارهای اضافی برای ساخت برنامه ها به ما می دهد. و ابزارهای اضافی به معنای افزایش انعطاف پذیری در توسعه نرم افزار است که به معنای گزینه های بیشتر برای حل یک مشکل خاص است.

اعمال AOP

برنامه نویسی جنبه گرا برای انجام وظایف متقاطع طراحی شده است، که می تواند هر کدی باشد که ممکن است بارها با روش های مختلف تکرار شود، که نمی توان به طور کامل در یک ماژول جداگانه ساختار داد. بر این اساس، AOP به ما اجازه می دهد این را خارج از کد اصلی نگه داریم و آن را به صورت عمودی اعلام کنیم. به عنوان مثال استفاده از یک سیاست امنیتی در یک برنامه کاربردی است. به طور معمول، امنیت از طریق بسیاری از عناصر یک برنامه اجرا می شود. علاوه بر این، خط مشی امنیتی برنامه باید به طور یکسان برای تمام بخش های موجود و جدید برنامه اعمال شود. در عین حال، یک سیاست امنیتی در حال استفاده خود می تواند تکامل یابد. این مکان عالی برای استفاده از AOP است . همچنین، مثال دیگر ورود به سیستم است . چندین مزیت برای استفاده از رویکرد AOP برای ورود به سیستم به جای افزودن دستی عملکرد گزارش وجود دارد:
  1. افزودن و حذف کد ورود به سیستم آسان است: تنها کاری که باید انجام دهید این است که چند پیکربندی از جنبه های مختلف را اضافه یا حذف کنید.

  2. تمام کد منبع برای ورود به سیستم در یک مکان نگهداری می شود، بنابراین نیازی نیست به صورت دستی همه مکان هایی که از آن استفاده می شود را جستجو کنید.

  3. کد ثبت‌نام را می‌توان در هر جایی اضافه کرد، چه در متدها و کلاس‌هایی که قبلاً نوشته شده‌اند یا در عملکردهای جدید. این باعث کاهش تعداد خطاهای کدنویسی می شود.

    همچنین، هنگام حذف یک جنبه از پیکربندی طراحی، می توانید مطمئن باشید که تمام کدهای ردیابی از بین رفته اند و چیزی از قلم نیفتاده است.

  4. جنبه‌ها کد جداگانه‌ای هستند که می‌توان آن‌ها را بهبود بخشید و بارها و بارها استفاده کرد.
AOP چیست؟  اصول برنامه نویسی جنبه گرا - 2AOP همچنین برای مدیریت استثنا، ذخیره سازی و استخراج عملکردهای خاص به منظور قابل استفاده مجدد استفاده می شود.

اصول اولیه AOP

برای حرکت بیشتر در این مبحث، اجازه دهید ابتدا با مفاهیم اصلی AOP آشنا شویم. مشاوره - منطق یا کد اضافی که از نقطه اتصال فراخوانی می شود. مشاوره را می توان قبل، بعد یا به جای نقطه اتصال انجام داد (در مورد آنها بیشتر در زیر). انواع مشاوره ممکن :
  1. قبل - این نوع مشاوره قبل از اجرای روش های هدف، یعنی نقاط اتصال، راه اندازی می شود. هنگام استفاده از جنبه‌ها به‌عنوان کلاس، از حاشیه‌نویسی @Before استفاده می‌کنیم تا توصیه‌های قبلی را علامت‌گذاری کنیم. هنگامی که از جنبه ها به عنوان فایل های aj استفاده می کنید، این روش ()پیش خواهد بود .

  2. پس از — توصیه ای که پس از اجرای متدها (نقاط پیوستن) کامل می شود، چه در اجرای عادی و چه در هنگام پرتاب یک استثنا، اجرا می شود.

    هنگام استفاده از جنبه ها به عنوان کلاس، می توانیم از حاشیه نویسی @After استفاده کنیم تا نشان دهیم که این توصیه ای است که بعد از آن ارائه می شود.

    هنگام استفاده از جنبه ها به عنوان فایل aj ، این روش after() است .

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

    وقتی جنبه‌ها به‌عنوان کلاس‌ها نشان داده می‌شوند، می‌توانیم از حاشیه‌نویسی @AfterReturning برای علامت‌گذاری توصیه‌ها به‌عنوان در حال اجرا پس از تکمیل موفقیت‌آمیز استفاده کنیم.

    هنگامی که از جنبه ها به عنوان فایل های aj استفاده می کنید، این روش پس از () بازگشت (Object obj) خواهد بود .

  4. بعد از پرتاب - این توصیه برای مواردی در نظر گرفته شده است که یک روش، یعنی نقطه اتصال، یک استثنا ایجاد می کند. ما می‌توانیم از این توصیه برای رسیدگی به انواع خاصی از اجرای ناموفق استفاده کنیم (به عنوان مثال، بازگرداندن کل تراکنش یا ورود به سیستم با سطح ردیابی مورد نیاز).

    برای جنبه های کلاس، حاشیه نویسی @AfterThrowing استفاده می شود تا نشان دهد که این توصیه پس از پرتاب یک استثنا استفاده می شود.

    هنگام استفاده از جنبه ها به عنوان فایل های aj ، این روش پرتاب after() (Exception e) خواهد بود .

  5. در اطراف - شاید یکی از مهم ترین انواع مشاوره. این یک متد را احاطه می کند، یعنی یک نقطه اتصال که می توانیم از آن استفاده کنیم، برای مثال، انتخاب کنیم که آیا یک روش نقطه اتصال داده شده را انجام دهیم یا نه.

    می توانید کد مشاوره ای بنویسید که قبل و بعد از اجرای متد join point اجرا می شود.

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

    با توجه به جنبه‌هایی به عنوان کلاس، از حاشیه‌نویسی @Around برای ایجاد توصیه‌هایی استفاده می‌کنیم که یک نقطه اتصال را می‌پیچد. هنگام استفاده از جنبه ها در قالب فایل های aj ، این متد متد () around خواهد بود .

Join Point - نقطه ای در یک برنامه در حال اجرا (به عنوان مثال فراخوانی متد، ایجاد شی، دسترسی متغیر) که در آن توصیه باید اعمال شود. به عبارت دیگر، این یک نوع عبارت منظم است که برای یافتن مکان‌هایی برای تزریق کد (مکان‌هایی که باید توصیه‌ها اعمال شود) استفاده می‌شود. Pointcut - مجموعه ای از نقاط اتصال . یک نقطه برش تعیین می کند که آیا توصیه داده شده برای یک نقطه اتصال داده شده قابل اجرا است یا خیر. جنبه - ماژول یا کلاسی که عملکرد متقاطع را پیاده سازی می کند. Aspect رفتار کد باقیمانده را با اعمال توصیه در نقاط اتصال تعریف شده توسط یک نقطه برش تغییر می دهد . به عبارت دیگر ترکیبی از پند و اندرز است. مقدمه - تغییر ساختار یک کلاس و/یا تغییر سلسله مراتب وراثت برای افزودن عملکرد جنبه به کد خارجی. هدف - شیئی که توصیه به آن اعمال خواهد شد. بافندگی - فرآیند پیوند دادن جنبه ها به اشیاء دیگر برای ایجاد اشیاء پروکسی توصیه شده. این را می توان در زمان کامپایل، زمان بارگذاری یا زمان اجرا انجام داد. سه نوع بافندگی وجود دارد:
  • بافندگی در زمان کامپایل - اگر کد منبع جنبه و کدی را که در آن از جنبه استفاده می کنید دارید، می توانید کد منبع و جنبه را مستقیماً با استفاده از کامپایلر AspectJ کامپایل کنید.

  • بافت پس از کامپایل (بافندگی باینری) - اگر نمی‌توانید یا نمی‌خواهید از تبدیل کد منبع برای بافتن جنبه‌ها در کد استفاده کنید، می‌توانید کلاس‌های کامپایل شده قبلی یا فایل‌های jar را انتخاب کنید و جنبه‌هایی را به آن‌ها تزریق کنید.

  • بافندگی در زمان بارگذاری - این فقط بافندگی باینری است که تا زمانی که کلاس‌لودر فایل کلاس را بارگیری کند و کلاس را برای JVM تعریف کند به تأخیر می‌افتد.

    یک یا چند لودر کلاس بافندگی برای پشتیبانی از این مورد نیاز است. آنها یا به صراحت توسط زمان اجرا ارائه می شوند یا توسط یک "عامل بافندگی" فعال می شوند.

AspectJ - یک پیاده سازی خاص از پارادایم AOP که توانایی انجام وظایف مقطعی را پیاده سازی می کند. اسناد را می توان در اینجا یافت .

نمونه هایی در جاوا

در مرحله بعد، برای درک بهتر AOP ، نمونه‌های کوچکی به سبک Hello World را بررسی می‌کنیم. در سمت راست خفاش، متذکر می شوم که نمونه های ما از بافت در زمان کامپایل استفاده می کنند . ابتدا باید وابستگی زیر را در فایل pom.xml خود اضافه کنیم :
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.9.5</version>
</dependency>
به عنوان یک قاعده، کامپایلر ویژه ajc نحوه استفاده ما از جنبه ها است. IntelliJ IDEA به طور پیش فرض شامل آن نمی شود، بنابراین هنگام انتخاب آن به عنوان کامپایلر برنامه، باید مسیر توزیع 5168 75 AspectJ را مشخص کنید . این اولین راه بود. دومی که من استفاده کردم این است که افزونه زیر را در فایل pom.xml ثبت کنم :
<build>
  <plugins>
     <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>1.7</version>
        <configuration>
           <complianceLevel>1.8</complianceLevel>
           <source>1.8</source>
           <target>1.8</target>
           <showWeaveInfo>true</showWeaveInfo>
           <<verbose>true<verbose>
           <Xlint>ignore</Xlint>
           <encoding>UTF-8</encoding>
        </configuration>
        <executions>
           <execution>
              <goals>
                 <goal>compile</goal>
                 <goal>test-compile</goal>
              </goals>
           </execution>
        </executions>
     </plugin>
  </plugins>
</build>
پس از این، ایده خوبی است که از Maven مجددا وارد کنید و mvn clean compile را اجرا کنید . حالا مستقیماً به سراغ مثال‌ها می‌رویم.

مثال شماره 1

بیایید یک کلاس اصلی ایجاد کنیم . در آن، یک نقطه ورودی و یک روش خواهیم داشت که یک نام ارسال شده را روی کنسول چاپ می کند:
public class Main {

  public static void main(String[] args) {
  printName("Tanner");
  printName("Victor");
  printName("Sasha");
  }

  public static void printName(String name) {
     System.out.println(name);
  }
}
اینجا هیچ چیز پیچیده ای نیست ما یک نام گذاشتیم و آن را روی کنسول نمایش دادیم. اگر اکنون برنامه را اجرا کنیم، موارد زیر را در کنسول مشاهده خواهیم کرد:
برنزه کننده ویکتور ساشا
اکنون وقت آن است که از قدرت AOP استفاده کنید. حالا باید یک فایل aspect بسازیم . آنها دو نوع هستند: اولی دارای پسوند فایل .aj است . دومی یک کلاس معمولی است که از حاشیه نویسی برای پیاده سازی قابلیت های AOP استفاده می کند . بیایید ابتدا به فایل با پسوند aj نگاه کنیم :
public aspect GreetingAspect {

  pointcut greeting() : execution(* Main.printName(..));

  before() : greeting() {
     System.out.print("Hi, ");
  }
}
این فایل به نوعی شبیه یک کلاس است. بیایید ببینیم اینجا چه اتفاقی می‌افتد: نقطه‌کات مجموعه‌ای از نقاط اتصال است. greeting() نام این نقطه کات است. : اجرا نشان می دهد که آن را در طول اجرای همه فراخوانی های ( * ) متد Main.printName(...) اعمال کنید . بعد یک توصیه خاص می آید - Before() - که قبل از فراخوانی متد هدف اجرا می شود. : greeting() نقطه برشی است که این توصیه به آن پاسخ می دهد. خوب و در زیر بدنه خود متد را می بینیم که به زبان جاوا نوشته شده است که ما آن را درک می کنیم. وقتی main را با این جنبه اجرا می کنیم ، این خروجی کنسول را دریافت می کنیم:
سلام، تنر سلام، ویکتور سلام، ساشا
می بینیم که هر فراخوانی به متد printName به لطف یک جنبه تغییر یافته است. حالا بیایید نگاهی بیندازیم که این جنبه به عنوان یک کلاس جاوا با حاشیه نویسی چگونه به نظر می رسد:
@Aspect
public class GreetingAspect{

  @Pointcut("execution(* Main.printName(String))")
  public void greeting() {
  }

  @Before("greeting()")
  public void beforeAdvice() {
     System.out.print("Hi, ");
  }
}
بعد از فایل جنبه aj ، همه چیز در اینجا واضح تر می شود:
  • @Aspect نشان می دهد که این کلاس یک جنبه است.
  • @Pointcut("execution(* Main.printName(String))") نقطه برشی است که برای همه فراخوانی های Main.printName با آرگومان ورودی که نوع آن String است راه اندازی می شود .
  • @Before("greeting()") توصیه ای است که قبل از فراخوانی کد مشخص شده در برش () greeting اعمال می شود .
اجرای main با این جنبه، خروجی کنسول را تغییر نمی دهد:
سلام، تنر سلام، ویکتور سلام، ساشا

مثال شماره 2

فرض کنید متدی داریم که برخی از عملیات را برای کلاینت ها انجام می دهد و این متد را از main فراخوانی می کنیم :
public class Main {

  public static void main(String[] args) {
  performSomeOperation("Tanner");
  }

  public static void performSomeOperation(String clientName) {
     System.out.println("Performing some operations for Client " + clientName);
  }
}
بیایید از حاشیه نویسی @Around برای ایجاد یک "شبه تراکنش" استفاده کنیم:
@Aspect
public class TransactionAspect{

  @Pointcut("execution(* Main.performSomeOperation(String))")
  public void executeOperation() {
  }

  @Around(value = "executeOperation()")
  public void beforeAdvice(ProceedingJoinPoint joinPoint) {
     System.out.println("Opening a transaction...");
     try {
        joinPoint.proceed();
        System.out.println("Closing a transaction...");
     }
     catch (Throwable throwable) {
        System.out.println("The operation failed. Rolling back the transaction...");
     }
  }
  }
با متد progress شی ProceedingJoinPoint ، متد wrapping را فراخوانی می کنیم تا محل آن را در مشاوره مشخص کنیم. بنابراین، کد موجود در متد بالا joinPoint.proceed(); قبل از است ، در حالی که کد زیر آن After است . اگر main را اجرا کنیم ، این را در کنسول دریافت می کنیم:
باز کردن تراکنش... انجام برخی عملیات برای Client Tanner بستن تراکنش...
اما اگر در روش خود (برای شبیه سازی یک عملیات ناموفق) پرتاب کنیم و استثنا کنیم:
public static void performSomeOperation(String clientName) throws Exception {
  System.out.println("Performing some operations for Client " + clientName);
  throw new Exception();
}
سپس این خروجی کنسول را دریافت می کنیم:
در حال باز کردن تراکنش... انجام برخی از عملیات برای Client Tanner عملیات انجام نشد. بازگرداندن معامله...
بنابراین آنچه در اینجا به آن رسیدیم نوعی قابلیت مدیریت خطا است.

مثال شماره 3

در مثال بعدی، بیایید کاری مانند ورود به کنسول انجام دهیم. ابتدا نگاهی به Main بیندازید ، جایی که ما برخی منطق شبه کسب و کار را اضافه کرده ایم:
public class Main {
  private String value;

  public static void main(String[] args) throws Exception {
     Main main = new Main();
     main.setValue("<some value>");
     String valueForCheck = main.getValue();
     main.checkValue(valueForCheck);
  }

  public void setValue(String value) {
     this.value = value;
  }

  public String getValue() {
     return this.value;
  }

  public void checkValue(String value) throws Exception {
     if (value.length() > 10) {
        throw new Exception();
     }
  }
}
در اصل ، ما از setValue برای اختصاص یک مقدار به متغیر نمونه مقدار استفاده می کنیم . سپس از getValue برای بدست آوردن مقدار استفاده می کنیم و سپس checkValue را فراخوانی می کنیم تا ببینیم بیشتر از 10 کاراکتر است یا خیر. اگر چنین است، پس از آن یک استثنا پرتاب خواهد شد. حالا بیایید به جنبه ای که برای ثبت کار متدها استفاده می کنیم نگاه کنیم:
@Aspect
public class LogAspect {

  @Pointcut("execution(* *(..))")
  public void methodExecuting() {
  }

  @AfterReturning(value = "methodExecuting()", returning = "returningValue")
  public void recordSuccessfulExecution(JoinPoint joinPoint, Object returningValue) {
     if (returningValue != null) {
        System.out.printf("Successful execution: method — %s method, class — %s class, return value — %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName(),
              returningValue);
     }
     else {
        System.out.printf("Successful execution: method — %s, class — %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName());
     }
  }

  @AfterThrowing(value = "methodExecuting()", throwing = "exception")
  public void recordFailedExecution(JoinPoint joinPoint, Exception exception) {
     System.out.printf("Exception thrown: method — %s, class — %s, exception — %s\n",
           joinPoint.getSignature().getName(),
           joinPoint.getSourceLocation().getWithinType().getName(),
           exception);
  }
}
اینجا چه خبره؟ @Pointcut("execution(* *(..))") به همه فراخوانی های همه متدها می پیوندد. @AfterReturning(value = "methodExecuting()، returning = "returningValue") توصیه ای است که پس از اجرای موفقیت آمیز متد هدف اجرا می شود. در اینجا دو مورد داریم:
  1. وقتی متد دارای مقدار بازگشتی باشد — if (returningValue! = Null) {
  2. وقتی مقدار برگشتی وجود ندارد — else {
@AfterThrowing(value = "methodExecuting()"، throwing = "exception") توصیه‌ای است که در صورت بروز خطا، یعنی زمانی که متد یک استثنا ایجاد می‌کند، راه‌اندازی می‌شود. و بر این اساس، با اجرای main ، نوعی لاگ مبتنی بر کنسول را دریافت خواهیم کرد:
اجرای موفقیت آمیز: متد — setValue، کلاس — اجرای موفقیت آمیز اصلی: متد — getValue، کلاس — Main، مقدار بازگشتی — <some value> استثنا پرتاب شده: متد — checkValue، کلاس — استثنای اصلی — java.lang.Exception استثنا پرتاب شده: متد — اصلی، کلاس - اصلی، استثنا - java.lang.Exception
و از آنجایی که ما استثناها را مدیریت نکردیم، همچنان یک stack trace دریافت خواهیم کرد: AOP چیست؟  اصول برنامه نویسی جنبه گرا - 3می‌توانید در مورد استثناها و مدیریت استثنا در این مقاله‌ها بخوانید: Exceptions در جاوا و Exceptions: catching and handling . امروز برای من همین است. امروز با AOP آشنا شدیم و شما توانستید ببینید که این جانور آنقدرها هم که برخی افراد تصور می کنند ترسناک نیست. خداحافظ همه!
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION