JavaRush /Java Blogu /Random-AZ /AOP nədir? Aspekt yönümlü proqramlaşdırmanın əsasları

AOP nədir? Aspekt yönümlü proqramlaşdırmanın əsasları

Qrupda dərc edilmişdir
Salam uşaqlar! Əsas anlayışları başa düşmədən, funksionallıq qurmaq üçün çərçivələri və yanaşmaları araşdırmaq olduqca çətindir. Beləliklə, bu gün biz bu anlayışlardan biri - AOP və ya aspekt yönümlü proqramlaşdırma haqqında danışacağıq . AOP nədir?  Aspekt yönümlü proqramlaşdırmanın əsasları - 1Bu asan mövzu deyil və tez-tez birbaşa istifadə edilmir, lakin bir çox çərçivə və texnologiya onu başlıq altında istifadə edir. Və təbii ki, bəzən müsahibələr zamanı sizdən bunun hansı heyvan olduğunu və harada istifadə oluna biləcəyini ümumi şəkildə söyləməyinizi xahiş edə bilərlər. Beləliklə, Java-da AOP-un əsas anlayışlarına və bəzi sadə nümunələrinə baxaq . AOP nədir?  Aspekt yönümlü proqramlaşdırmanın əsasları - 2Beləliklə, AOP - aspekt yönümlü proqramlaşdırma - kəsişən narahatlıqları ayırmaqla tətbiqin müxtəlif hissələrinin modulluğunu artırmağa yönəlmiş bir paradiqmadır. Bunun üçün orijinal kodu dəyişdirmədən mövcud koda əlavə davranış əlavə olunur. Başqa sözlə, biz dəyişdirilmiş koda düzəlişlər etmədən metod və siniflərin üzərinə əlavə funksionallıq asırıq. Bu niyə lazımdır? Gec-tez belə bir nəticəyə gəlirik ki, adi obyekt yönümlü yanaşma həmişə müəyyən problemləri effektiv şəkildə həll edə bilmir. Belə bir anda AOP köməyə gəlir və tətbiqi qurmaq üçün bizə əlavə vasitələr verir. Əlavə vasitələr inkişafda artan çeviklik deməkdir, bunun sayəsində müəyyən bir problemi həll etmək üçün daha çox seçim var.

AOP tətbiqi

Aspekt yönümlü proqramlaşdırma, müxtəlif üsullarla dəfələrlə təkrarlanan, tamamilə ayrıca modulda strukturlaşdırıla bilməyən istənilən kod ola bilən kəsişən problemləri həll etmək üçün nəzərdə tutulmuşdur. Müvafiq olaraq, AOP ilə bunu əsas koddan kənarda qoyub şaquli olaraq təyin edə bilərik. Tətbiqdə təhlükəsizlik siyasətinin tətbiqi nümunədir. Tipik olaraq, təhlükəsizlik proqramın bir çox elementini kəsir. Bundan əlavə, tətbiqin təhlükəsizlik siyasəti tətbiqin bütün mövcud və yeni hissələrinə bərabər şəkildə tətbiq edilməlidir. Eyni zamanda, istifadə olunan təhlükəsizlik siyasəti özü də inkişaf edə bilər. AOP istifadəsinin lazımlı ola biləcəyi yer budur . Həmçinin başqa bir misal girişdir . Girişin əl ilə daxil edilməsi ilə müqayisədə giriş üçün AOP yanaşmasından istifadə etməyin bir sıra üstünlükləri var:
  1. Giriş kodunu həyata keçirmək və silmək asandır: sadəcə bəzi aspektlərin bir neçə konfiqurasiyasını əlavə etmək və ya silmək lazımdır.
  2. Giriş üçün bütün mənbə kodu bir yerdə saxlanılır və bütün istifadə yerlərini əl ilə tapmağa ehtiyac yoxdur.
  3. Giriş üçün nəzərdə tutulmuş kod istənilən yerə əlavə edilə bilər, istər artıq yazılmış metodlar və siniflər, istərsə də yeni funksionallıq. Bu, tərtibatçı səhvlərinin sayını azaldır.
    Həmçinin, dizayn konfiqurasiyasından bir aspekti sildiyiniz zaman bütün iz kodunun silindiyinə və heç bir şeyin əskik olmadığına tam əmin ola bilərsiniz.
  4. Aspektlər təkrar istifadə oluna və təkmilləşdirilə bilən müstəqil kodlardır.
AOP nədir?  Aspekt yönümlü proqramlaşdırmanın əsasları - 3AOP, həmçinin təkrar istifadə edilə bilən hala gətirmək üçün istisnaların idarə edilməsi, keşləşdirilməsi və bəzi funksiyaların silinməsi üçün istifadə olunur.

AOP-un əsas anlayışları

Mövzunun təhlilində daha da irəli getmək üçün əvvəlcə AOP-un əsas anlayışları ilə tanış olaq. Məsləhət əlaqə nöqtəsindən çağırılan əlavə məntiqdir, koddur. Məsləhət əlaqə nöqtəsindən əvvəl, sonra və ya yerinə yerinə yetirilə bilər (aşağıda daha çox). Mümkün məsləhət növləri :
  1. Əvvəl (əvvəl) - bu tip məsləhətlər hədəf metodların - əlaqə nöqtələrinin icrasından əvvəl işə salınır. Aspektləri siniflər kimi istifadə edərkən, məsləhət növünü əvvəlki kimi qeyd etmək üçün @Before annotasiyasını götürürük . Aspektləri .aj faylları kimi istifadə edərkən bu, before() metodu olacaq .
  2. Sonra (Sonra) - metodların icrası başa çatdıqdan sonra yerinə yetirilən məsləhət - həm normal hallarda, həm də istisna atıldıqda əlaqə nöqtələri. Aspektləri siniflər kimi istifadə edərkən, bunun sonra gələn bir ipucu olduğunu göstərmək üçün @After
    annotasiyasından istifadə edə bilərik . Aspektləri .aj faylları kimi istifadə edərkən bu after() metodu olacaq .
  3. Qayıdandan sonra - bu məsləhətlər yalnız hədəf metodu normal, səhvsiz işlədiyi halda yerinə yetirilir.
    Aspektlər siniflər kimi təqdim edildikdə, müvəffəqiyyətlə tamamlandıqdan sonra tövsiyənin yerinə yetirildiyini qeyd etmək üçün @AfterReturning annotasiyasından istifadə edə bilərik.
    Aspektləri .aj faylları kimi istifadə edərkən, bu, geri qayıdan after() metodu olacaq (Obyekt obyekti) .
  4. Atmadan sonra - bu tip məsləhət metodun, yəni əlaqə nöqtəsinin bir istisna atdığı hallar üçün nəzərdə tutulub. Biz bu məsləhətdən uğursuz icranın bəzi idarə edilməsi üçün istifadə edə bilərik (məsələn, bütün tranzaksiyanı geri qaytarmaq və ya tələb olunan izləmə səviyyəsi ilə qeyd etmək).
    Aspekt sinifləri üçün @AfterThrowing annotasiyası bu məsləhətin istisna atıldıqdan sonra istifadə edildiyini göstərmək üçün istifadə olunur. .aj
    faylları şəklində aspektlərdən istifadə edərkən , bu metod olacaq - after() atma (İstisna e) .
  5. Around, bəlkə də bir metodu əhatə edən ən vacib məsləhət növlərindən biridir, yəni əlaqə nöqtəsidir, onunla, məsələn, verilmiş bir əlaqə nöqtəsi metodunu icra edib etməməyi seçə bilərik.
    Qoşulma nöqtəsi metodunun icrasından əvvəl və sonra işləyən məsləhət kodunu yaza bilərsiniz. Məsləhətçinin
    məsuliyyətlərinə qoşulma nöqtəsi metodunu çağırmaq və metod bir şey qaytararsa, dəyərləri qaytarmaq daxildir. Yəni, bu ipucuda siz sadəcə olaraq hədəf metodunun işləməsini onu çağırmadan təqlid edə və nəticədə özünüzdən nəsə qaytara bilərsiniz. Siniflər şəklində olan aspektlər üçün əlaqə nöqtəsini əhatə edən məsləhətlər yaratmaq üçün @Around annotasiyasından istifadə edirik. Aspektləri .aj faylları kimi istifadə edərkən bu, around() metodu olacaq .
Qoşulma nöqtəsi – icraçı proqramda məsləhətin tətbiq edilməli olduğu nöqtə (metod çağırmaq, obyekt yaratmaq, dəyişənə daxil olmaq). Başqa sözlə, bu, kodun tətbiqi üçün yerlər (məsləhətlərin tətbiqi üçün yerlər) tapıldığı bir növ müntəzəm ifadədir. Pointcut əlaqə nöqtələrinin toplusudur . Kəsmə müəyyən bir əlaqə nöqtəsinin verilmiş bir ucun uyğun olub olmadığını müəyyən edir. Aspekt sondan sona funksionallığı həyata keçirən modul və ya sinifdir. Bir aspekt bəzi dilimlə müəyyən edilmiş birləşmə nöqtələrində məsləhətlər tətbiq etməklə kodun qalan hissəsinin davranışını dəyişdirir . Başqa sözlə, məsləhətlər və əlaqə nöqtələrinin birləşməsidir. Giriş - xarici koda aspekt funksionallığı əlavə etmək üçün sinfin strukturunun dəyişdirilməsi və/yaxud miras iyerarxiyasının dəyişdirilməsi. Hədəf məsləhətin tətbiq olunacağı obyektdir. Toxuculuq tövsiyə olunan proxy obyektləri yaratmaq üçün aspektləri digər obyektlərlə əlaqələndirmək prosesidir. Bu, kompilyasiya zamanı, yükləmə zamanı və ya işləmə müddətində edilə bilər. Üç növ toxuculuq var:
  • Compile-time weaving - Əgər aspektin mənbə koduna və aspektlərdən istifadə etdiyiniz kodunuza sahibsinizsə, mənbə kodunu və aspekti birbaşa AspectJ kompilyatorundan istifadə edərək tərtib edə bilərsiniz;
  • kompilyasiyadan sonrakı toxuculuq (binar toxuculuq) - aspektləri kodunuza toxundurmaq üçün mənbə kodu transformasiyalarından istifadə edə bilmirsinizsə və ya istifadə etmək istəmirsinizsə, siz artıq tərtib edilmiş dərsləri və ya bankaları götürə və aspektləri inyeksiya edə bilərsiniz;
  • yükləmə zamanı toxuculuq sinif yükləyicisi sinif faylını yükləyənə və JVM üçün sinfi təyin edənə qədər təxirə salınan sadəcə ikili toxuculuqdur.
    Bunu dəstəkləmək üçün bir və ya bir neçə "toxuculuq sinfi yükləyicisi" tələb olunur. Onlar ya açıq şəkildə iş vaxtı ilə təmin edilir və ya "toxuculuq agenti" tərəfindən aktivləşdirilir.
AspectJ, kəsişən problemləri həll etmək qabiliyyətini həyata keçirən AOP paradiqmalarının xüsusi tətbiqidir . Sənədləri burada tapa bilərsiniz .

Java-da nümunələr

Sonra, AOP-u daha yaxşı başa düşmək üçün Hello World səviyyəsinin kiçik nümunələrinə baxacağıq. AOP nədir?  Aspekt yönümlü proqramlaşdırmanın əsasları - 4Dərhal qeyd edim ki, nümunələrimizdə kompilyasiya zamanı toxuculuqdan istifadə edəcəyik . Əvvəlcə pom.xml faylımıza aşağıdakı asılılığı əlavə etməliyik :
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.9.5</version>
</dependency>
Bir qayda olaraq, aspektlərdən istifadə etmək üçün xüsusi Ajs kompilyatorundan istifadə olunur . IntelliJ IDEA- da standart olaraq bu yoxdur, ona görə də onu proqram tərtibçisi kimi seçərkən AspectJ paylanmasına gedən yolu göstərməlisiniz . Ajs-ın tərtibçi kimi seçilməsi üsulu haqqında bu səhifədə ətraflı oxuya bilərsiniz. Bu, birinci üsul idi və ikinci (istifadə etdiyim) aşağıdakı plaqini pom.xml -ə əlavə etmək idi :
<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>
Bundan sonra, Maven- dən yenidən idxal etmək və mvn clean compile-ni işə salmaq məsləhətdir . İndi nümunələrə keçək.

Nümunə № 1

Gəlin Əsas sinif yaradaq . Orada bir başlanğıc nöqtəsi və ona ötürülən adları konsolda çap edən bir üsulumuz olacaq:
public class Main {

  public static void main(String[] args) {
  printName("Толя");
  printName("Вова");
  printName("Sasha");
  }

  public static void printName(String name) {
     System.out.println(name);
  }
}
Mürəkkəb bir şey yoxdur: adı keçirdilər və konsolda göstərdilər. İndi işə salsaq, konsol göstərəcək:
Tolya Vova Saşa
Yaxşı, AOP-in gücündən istifadə etməyin vaxtı gəldi. İndi bir fayl aspekti yaratmalıyıq . Onlar iki növdə olur: birincisi .aj uzantılı fayldır , ikincisi annotasiyalardan istifadə edərək AOP imkanlarını həyata keçirən adi sinifdir . Əvvəlcə .aj uzantılı fayla baxaq :
public aspect GreetingAspect {

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

  before() : greeting() {
     System.out.print("Привет ");
  }
}
Bu fayl bir qədər sinfə bənzəyir. Burada nə baş verdiyini anlayaq: pointcut - kəsik və ya əlaqə nöqtələrinin dəsti; salamlama() — bu dilimin adı; : icra - icra edərkən * - hamısı, zəng - Main.printName(..) - bu üsul. Sonra xüsusi məsləhət gəlir - before() - hədəf metodu çağırılmadan əvvəl yerinə yetirilir, : salamlama() - bu məsləhətin reaksiya verdiyi dilim və aşağıda Java-da yazılmış metodun özünün gövdəsini görürük. anladığımız dildir. Mövcud olan bu aspektlə əsas işlətdiyimiz zaman konsola aşağıdakı çıxışı alacağıq:
Salam Tolya Salam Vova Salam Sasha
PrintName metoduna edilən hər çağırışın bir aspektlə dəyişdirildiyini görə bilərik . İndi aspektin necə görünəcəyinə nəzər salaq, lakin qeydləri olan bir Java sinfi olaraq:
@Aspect
public class GreetingAspect{

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

  @Before("greeting()")
  public void beforeAdvice() {
     System.out.print("Привет ");
  }
}
.aj aspect faylından sonra hər şey daha aydın görünür:
  • @Aspect verilmiş sinfin aspekt olduğunu bildirir;
  • @Pointcut("execution(* Main.printName(String))") String tipli daxil olan arqumentlə Main.printName-ə edilən bütün zəngləri işə salan kəsmə nöqtəsidir ;
  • @Before("salamlama()") - salamlama() kəsmə nöqtəsində təsvir olunan kodu çağırmadan əvvəl tətbiq olunan məsləhətdir .
Bu aspektlə main-i işə salmaq konsol çıxışını dəyişməyəcək:
Salam Tolya Salam Vova Salam Sasha

Nümunə № 2

Tutaq ki, müştərilər üçün bəzi əməliyyatları yerinə yetirən bəzi metodumuz var və bu metodu main -dən çağırırıq :
public class Main {

  public static void main(String[] args) {
  makeSomeOperation("Толя");
  }

  public static void makeSomeOperation(String clientName) {
     System.out.println("Выполнение некоторых операций для клиента - " + clientName);
  }
}
@Around annotasiyasından istifadə edərək gəlin “psevdotransaction” kimi bir şey edək:
@Aspect
public class TransactionAspect{

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

  @Around(value = "executeOperation()")
  public void beforeAdvice(ProceedingJoinPoint joinPoint) {
     System.out.println("Открытие транзакции...");
     try {
        joinPoint.proceed();
        System.out.println("Закрытие транзакции....");
     }
     catch (Throwable throwable) {
        System.out.println("Операция не удалась, откат транзакции...");
     }
  }
  }
ProceedingJoinPoint obyektinin davam metodundan istifadə edərək , onun lövhədəki yerini və müvafiq olaraq yuxarıdakı metoddakı kodu müəyyən etmək üçün sarğı metodunu çağırırıq joinPoint.proceed(); - bu əvvəl , aşağıda olan - sonra . Main-i işlədirsək, konsola daxil olacağıq:
Əməliyyatın açılması... Müştəri üçün bəzi əməliyyatların aparılması - Tolya Əməliyyatın bağlanması....
Metodumuza istisna atışı əlavə etsək (birdən əməliyyat uğursuz olur):
public static void makeSomeOperation(String clientName)throws Exception {
  System.out.println("Выполнение некоторых операций для клиента - " + clientName);
  throw new Exception();
}
Sonra konsolda çıxışı alacağıq:
Əməliyyat açılır... Müştəri üçün bəzi əməliyyatlar aparılır - Tolya Əməliyyat uğursuz oldu, əməliyyat geri çəkildi...
Bu, uğursuzluğun psevdo-emalı olduğu ortaya çıxdı.

Nümunə № 3

Növbəti nümunə olaraq, konsola daxil olmaq kimi bir şey edək. Əvvəlcə psevdo biznes məntiqimizin baş verdiyi Main- ə baxaq :
public class Main {
  private String value;

  public static void main(String[] args) throws Exception {
     Main main = new Main();
     main.setValue("<некоторое meaning>");
     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();
     }
  }
}
Əsas olaraq setValue istifadə edərək daxili dəyişənin dəyərini təyin edəcəyik - value , sonra getValue istifadə edərək bu dəyəri götürəcəyik və checkValue- də bu dəyərin 10 simvoldan uzun olub olmadığını yoxlayacağıq. Əgər belədirsə, istisna atılacaq. İndi metodların işləməsini qeyd edəcəyimiz aspektə baxaq:
@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("Успешно выполнен метод - %s, класса- %s, с результатом выполнения - %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName(),
              returningValue);
     }
     else {
        System.out.printf("Успешно выполнен метод - %s, класса- %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName());
     }
  }

  @AfterThrowing(value = "methodExecuting()", throwing = "exception")
  public void recordFailedExecution(JoinPoint joinPoint, Exception exception) {
     System.out.printf("Метод - %s, класса- %s, был аварийно завершен с исключением - %s\n",
           joinPoint.getSignature().getName(),
           joinPoint.getSourceLocation().getWithinType().getName(),
           exception);
  }
}
Burda nə baş verir? @Pointcut("execution(* *(..))") - bütün metodlara bütün zənglərə qoşulacaq; @AfterReturning(value = "methodExecuting()", returning = "returningValue") - hədəf metodu uğurla başa çatdıqdan sonra yerinə yetiriləcək məsləhət. Burada iki halımız var:
  1. Metodun qaytarma dəyəri olduqda əgər (returningValue != null) {
  2. Başqa heç bir qaytarma dəyəri olmadıqda {
@AfterThrowing(value = "methodExecuting()", throwing = "istisna") - xəta baş verdikdə, yəni metoddan istisna atıldıqda işə salınacaq məsləhət. Buna görə də, main işlətməklə , konsolda bir növ giriş əldə edəcəyik:
Metod - setValue, sinifin - Main müvəffəqiyyətlə icra edildi.. Metod - getValue, sinifin - Main, müvəffəqiyyətlə icra edildi, icra nəticəsində - <some value> Metod - checkValue, sinfin - Main, istisna ilə anormal şəkildə dayandırıldı - java.lang.Exception Metod - əsas, sinif-Main, istisna ilə qəzaya uğradı - java.lang.Exception
Yaxşı, biz istisnanı idarə etmədiyimiz üçün onun stacktrace-ini də əldə edəcəyik: AOP nədir?  Aspekt yönümlü proqramlaşdırmanın əsasları - 5İstisnalar və onların işlənməsi haqqında bu məqalələrdə oxuya bilərsiniz: Java-da İstisnalarİstisnalar və onların işlənməsi . Bu gün mənim üçün hamısı budur. Bu gün biz AOP ilə tanış olduq və siz bu heyvanın rəngləndiyi qədər qorxulu olmadığını görə bilərsiniz. Hər kəsə əlvida!AOP nədir?  Aspekt yönümlü proqramlaşdırmanın əsasları - 6
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION