CodeGym /Blog Java /rawak /Apakah AOP? Prinsip pengaturcaraan berorientasikan aspek
John Squirrels
Tahap
San Francisco

Apakah AOP? Prinsip pengaturcaraan berorientasikan aspek

Diterbitkan dalam kumpulan
Hai, kawan-kawan dan perempuan! Tanpa memahami konsep asas, agak sukar untuk menyelidiki rangka kerja dan pendekatan untuk membina kefungsian. Jadi hari ini kita akan bercakap tentang satu konsep sedemikian — AOP, aka pengaturcaraan berorientasikan aspek . Apakah AOP?  Prinsip pengaturcaraan berorientasikan aspek - 1Topik ini tidak mudah dan jarang digunakan secara langsung, tetapi banyak rangka kerja dan teknologi menggunakannya di bawah hud. Dan sudah tentu, kadangkala semasa temu bual, anda mungkin diminta untuk menerangkan secara umum jenis binatang ini dan di mana ia boleh digunakan. Jadi mari kita lihat konsep asas dan beberapa contoh mudah AOP dalam Java . Sekarang, AOP bermaksud pengaturcaraan berorientasikan aspek, yang merupakan paradigma yang bertujuan untuk meningkatkan kemodulatan bahagian-bahagian yang berbeza aplikasi dengan memisahkan kebimbangan silang. Untuk mencapai ini, gelagat tambahan ditambahkan pada kod sedia ada tanpa membuat perubahan pada kod asal. Dalam erti kata lain, kita boleh menganggapnya sebagai menggantung fungsi tambahan di atas kaedah dan kelas tanpa mengubah kod yang diubah suai. Mengapa ini perlu? Lambat laun, kami menyimpulkan bahawa pendekatan berorientasikan objek biasa tidak boleh selalu menyelesaikan masalah tertentu dengan berkesan. Dan apabila saat itu tiba, AOP datang untuk menyelamatkan dan memberi kami alat tambahan untuk membina aplikasi. Dan alat tambahan bermakna peningkatan fleksibiliti dalam pembangunan perisian, yang bermaksud lebih banyak pilihan untuk menyelesaikan masalah tertentu.

Memohon AOP

Pengaturcaraan berorientasikan aspek direka bentuk untuk melaksanakan tugas memotong silang, yang boleh berupa sebarang kod yang mungkin diulang berkali-kali dengan kaedah yang berbeza, yang tidak boleh distruktur sepenuhnya ke dalam modul yang berasingan. Oleh itu, AOP membenarkan kami menyimpannya di luar kod utama dan mengisytiharkannya secara menegak. Contohnya ialah menggunakan dasar keselamatan dalam aplikasi. Biasanya, keselamatan berjalan melalui banyak elemen aplikasi. Lebih-lebih lagi, dasar keselamatan aplikasi harus digunakan secara sama rata pada semua bahagian aplikasi yang sedia ada dan baharu. Pada masa yang sama, dasar keselamatan yang digunakan boleh berkembang dengan sendirinya. Ini ialah tempat yang sesuai untuk menggunakan AOP . Juga, contoh lain ialah pembalakan. Terdapat beberapa kelebihan untuk menggunakan pendekatan AOP untuk pengelogan dan bukannya menambah fungsi pengelogan secara manual:
  1. Kod untuk pengelogan mudah untuk ditambah dan dialih keluar: semua yang anda perlu lakukan ialah menambah atau mengalih keluar beberapa konfigurasi beberapa aspek.

  2. Semua kod sumber untuk pembalakan disimpan di satu tempat, jadi anda tidak perlu memburu secara manual semua tempat di mana ia digunakan.

  3. Kod pengelogan boleh ditambah di mana-mana, sama ada dalam kaedah dan kelas yang telah ditulis atau dalam fungsi baharu. Ini mengurangkan bilangan ralat pengekodan.

    Selain itu, apabila mengalih keluar aspek daripada konfigurasi reka bentuk, anda boleh memastikan bahawa semua kod pengesanan telah hilang dan tiada apa yang terlepas.

  4. Aspek ialah kod berasingan yang boleh diperbaiki dan digunakan berulang kali.
Apakah AOP?  Prinsip pengaturcaraan berorientasikan aspek - 2AOP juga digunakan untuk pengendalian pengecualian, caching dan mengekstrak fungsi tertentu untuk menjadikannya boleh digunakan semula.

Prinsip asas AOP

Untuk bergerak lebih jauh dalam topik ini, mari kita kenali konsep utama AOP. Nasihat — Logik atau kod tambahan dipanggil dari titik gabungan. Nasihat boleh dilakukan sebelum, selepas, atau bukannya titik gabungan (lebih lanjut mengenainya di bawah). Jenis nasihat yang mungkin :
  1. Sebelum — jenis nasihat ini dilancarkan sebelum kaedah sasaran, iaitu mata gabungan, dilaksanakan. Apabila menggunakan aspek sebagai kelas, kami menggunakan anotasi @Before untuk menandakan nasihat sebagai datang sebelum ini. Apabila menggunakan aspek sebagai fail .aj , ini akan menjadi kaedah before() .

  2. Selepas — nasihat yang dilaksanakan selepas pelaksanaan kaedah (menyertai mata) selesai, baik dalam pelaksanaan biasa dan juga semasa melontar pengecualian.

    Apabila menggunakan aspek sebagai kelas, kita boleh menggunakan anotasi @After untuk menunjukkan bahawa ini adalah nasihat yang datang selepas itu.

    Apabila menggunakan aspek sebagai fail .aj , ini ialah kaedah selepas() .

  3. Selepas Kembali — nasihat ini dilakukan hanya apabila kaedah sasaran selesai seperti biasa, tanpa ralat.

    Apabila aspek diwakili sebagai kelas, kita boleh menggunakan anotasi @AfterReturning untuk menandakan nasihat sebagai dilaksanakan selepas berjaya disiapkan.

    Apabila menggunakan aspek sebagai fail .aj , ini akan menjadi kaedah after() returning (Object obj) .

  4. Selepas Melontar — nasihat ini bertujuan untuk contoh apabila kaedah, iaitu titik gabungan, melemparkan pengecualian. Kami boleh menggunakan nasihat ini untuk mengendalikan beberapa jenis pelaksanaan yang gagal (contohnya, untuk melancarkan keseluruhan transaksi atau log dengan tahap jejak yang diperlukan).

    Untuk aspek kelas, anotasi @AfterThrowing digunakan untuk menunjukkan bahawa nasihat ini digunakan selepas membuang pengecualian.

    Apabila menggunakan aspek sebagai fail .aj , ini akan menjadi kaedah lontaran selepas() (Pengecualian e) .

  5. Sekitar — mungkin salah satu jenis nasihat yang paling penting. Ia mengelilingi kaedah, iaitu titik cantum yang boleh kita gunakan untuk, contohnya, memilih sama ada untuk melaksanakan kaedah titik cantum yang diberikan atau tidak.

    Anda boleh menulis kod nasihat yang dijalankan sebelum dan selepas kaedah titik gabungan dilaksanakan.

    Nasihat sekitar bertanggungjawab untuk memanggil kaedah titik gabungan dan nilai pulangan jika kaedah itu mengembalikan sesuatu. Dalam erti kata lain, dalam nasihat ini, anda hanya boleh mensimulasikan operasi kaedah sasaran tanpa memanggilnya, dan mengembalikan apa sahaja yang anda inginkan sebagai hasil pulangan.

    Memandangkan aspek sebagai kelas, kami menggunakan anotasi @Around untuk membuat nasihat yang membungkus titik gabungan. Apabila menggunakan aspek dalam bentuk fail .aj , kaedah ini akan menjadi kaedah around() .

Sertai Point — titik dalam program yang sedang berjalan (iaitu panggilan kaedah, penciptaan objek, akses berubah-ubah) di mana nasihat harus digunakan. Dalam erti kata lain, ini adalah sejenis ungkapan biasa yang digunakan untuk mencari tempat suntikan kod (tempat di mana nasihat harus digunakan). Pointcut — satu set titik cantuman . Potongan titik menentukan sama ada nasihat yang diberikan boleh digunakan pada titik gabungan tertentu. Aspek — modul atau kelas yang melaksanakan fungsi silang. Aspek mengubah tingkah laku kod yang tinggal dengan menggunakan nasihat pada titik gabungan yang ditakrifkan oleh beberapa titik potong . Dalam erti kata lain, ia adalah gabungan nasihat dan mata gabungan. pengenalan— menukar struktur kelas dan/atau menukar hierarki warisan untuk menambah fungsi aspek kepada kod asing. Sasaran — objek yang nasihat akan digunakan. Tenunan — proses menghubungkan aspek ke objek lain untuk mencipta objek proksi yang dinasihatkan. Ini boleh dilakukan pada masa penyusunan, masa muat, atau masa jalankan. Terdapat tiga jenis tenunan:
  • Anyaman masa kompilasi — jika anda mempunyai kod sumber aspek dan kod tempat anda menggunakan aspek tersebut, maka anda boleh menyusun kod sumber dan aspek secara terus menggunakan pengkompil AspectJ;

  • Tenunan pasca kompilasi (anyaman binari) — jika anda tidak boleh atau tidak mahu menggunakan transformasi kod sumber untuk menganyam aspek ke dalam kod, anda boleh mengambil kelas yang disusun sebelum ini atau fail balang dan menyuntik aspek ke dalamnya;

  • Tenunan masa muat — ini hanyalah anyaman binari yang ditangguhkan sehingga pemuat kelas memuatkan fail kelas dan mentakrifkan kelas untuk JVM.

    Satu atau lebih pemuat kelas tenunan diperlukan untuk menyokong ini. Ia sama ada secara eksplisit disediakan oleh masa jalan atau diaktifkan oleh "agen tenunan."

AspectJ — Perlaksanaan khusus paradigma AOP yang melaksanakan keupayaan untuk melaksanakan tugasan silang. Dokumentasi boleh didapati di sini .

Contoh di Jawa

Seterusnya, untuk pemahaman yang lebih baik tentang AOP , kita akan melihat contoh kecil gaya "Hello World". Di sebelah kanan kelawar, saya akan ambil perhatian bahawa contoh kami akan menggunakan anyaman masa kompilasi . Pertama, kami perlu menambah kebergantungan berikut dalam fail pom.xml kami :

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.9.5</version>
</dependency>
Sebagai peraturan, pengkompil ajc khas ialah cara kami menggunakan aspek. IntelliJ IDEA tidak memasukkannya secara lalai, jadi apabila memilihnya sebagai pengkompil aplikasi, anda mesti menentukan laluan ke pengedaran 5168 75 AspectJ . Ini adalah cara pertama. Yang kedua, yang saya gunakan, adalah untuk mendaftarkan pemalam berikut dalam fail 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>
Selepas ini, adalah idea yang baik untuk mengimport semula daripada Maven dan menjalankan mvn clean compile . Sekarang mari kita teruskan terus ke contoh.

Contoh No. 1

Mari buat kelas Utama . Di dalamnya, kami akan mempunyai titik masuk dan kaedah yang mencetak nama yang diluluskan pada konsol:

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);
  }
}
Tidak ada yang rumit di sini. Kami lulus nama dan memaparkannya pada konsol. Jika kita menjalankan program sekarang, kita akan melihat perkara berikut pada konsol:
Tanner Victor Sasha
Sekarang, tiba masanya untuk memanfaatkan kuasa AOP. Sekarang kita perlu membuat fail aspek . Ia terdiri daripada dua jenis: yang pertama mempunyai sambungan fail .aj . Yang kedua ialah kelas biasa yang menggunakan anotasi untuk melaksanakan keupayaan AOP . Mari kita lihat dahulu fail dengan sambungan .aj :

public aspect GreetingAspect {
 
  pointcut greeting() : execution(* Main.printName(..));
 
  before() : greeting() {
     System.out.print("Hi, ");
  }
}
Fail ini agak seperti kelas. Mari lihat apa yang berlaku di sini: pointcut ialah satu set mata gabungan; greeting() ialah nama pointcut ini; : pelaksanaan menunjukkan untuk menerapkannya semasa pelaksanaan semua ( * ) panggilan kaedah Main.printName(...) . Seterusnya datang nasihat khusus — before() — yang dilaksanakan sebelum kaedah sasaran dipanggil. : greeting() ialah titik potong yang dijawab oleh nasihat ini. Nah, dan di bawah kita melihat badan kaedah itu sendiri, yang ditulis dalam bahasa Jawa, yang kita fahami. Apabila kita menjalankan utama dengan aspek ini hadir, kita akan mendapat output konsol ini:
Hai, Tanner Hai, Victor Hai, Sasha
Kita dapat melihat bahawa setiap panggilan ke kaedah printName telah diubah suai terima kasih kepada satu aspek. Sekarang mari kita lihat bagaimana aspek itu akan kelihatan sebagai kelas Java dengan anotasi:

@Aspect
public class GreetingAspect{
 
  @Pointcut("execution(* Main.printName(String))")
  public void greeting() {
  }
 
  @Before("greeting()")
  public void beforeAdvice() {
     System.out.print("Hi, ");
  }
}
Selepas fail aspek .aj , semuanya menjadi lebih jelas di sini:
  • @Aspect menunjukkan bahawa kelas ini adalah aspek;
  • @Pointcut("execution(* Main.printName(String))") ialah titik potong yang dicetuskan untuk semua panggilan ke Main.printName dengan argumen input yang jenisnya String ;
  • @Before("greeting()") ialah nasihat yang digunakan sebelum memanggil kod yang dinyatakan dalam greeting() cutpoint.
Menjalankan utama dengan aspek ini tidak mengubah output konsol:
Hai, Tanner Hai, Victor Hai, Sasha

Contoh No. 2

Katakan kami mempunyai beberapa kaedah yang melaksanakan beberapa operasi untuk pelanggan, dan kami memanggil kaedah ini dari 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);
  }
}
Mari kita gunakan anotasi @Around untuk membuat "pseudo-transaction":

@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...");
     }
  }
  }
Dengan kaedah proceed objek ProceedingJoinPoint , kami memanggil kaedah pembalut untuk menentukan lokasinya dalam nasihat. Oleh itu, kod dalam kaedah di atas joinPoint.proceed(); ialah Sebelum , manakala kod di bawahnya ialah Selepas . Jika kita menjalankan main , kita mendapat ini dalam konsol:
Membuka transaksi... Menjalankan beberapa operasi untuk Penyamak Pelanggan Menutup transaksi...
Tetapi jika kita membuang dan pengecualian dalam kaedah kita (untuk mensimulasikan operasi yang gagal):

public static void performSomeOperation(String clientName) throws Exception {
  System.out.println("Performing some operations for Client " + clientName);
  throw new Exception();
}
Kemudian kami mendapat output konsol ini:
Membuka transaksi... Menjalankan beberapa operasi untuk Penyamak Pelanggan Operasi gagal. Melancarkan semula transaksi...
Jadi apa yang kami dapati di sini ialah sejenis keupayaan pengendalian ralat.

Contoh No. 3

Dalam contoh seterusnya, mari kita lakukan sesuatu seperti log masuk ke konsol. Mula-mula, lihat Main , di mana kami telah menambah beberapa logik perniagaan pseudo:

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();
     }
  }
}
Dalam main , kami menggunakan setValue untuk menetapkan nilai kepada pembolehubah contoh nilai . Kemudian kami menggunakan getValue untuk mendapatkan nilai, dan kemudian kami memanggil checkValue untuk melihat sama ada ia lebih daripada 10 aksara. Jika ya, maka pengecualian akan dilemparkan. Sekarang mari kita lihat aspek yang akan kita gunakan untuk log kerja kaedah:

@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);
  }
}
Apa yang berlaku di sini? @Pointcut("execution(* *(..))") akan menyertai semua panggilan semua kaedah. @AfterReturning(value = "methodExecuting()", returning = "returningValue") ialah nasihat yang akan dilaksanakan selepas berjaya melaksanakan kaedah sasaran. Kami mempunyai dua kes di sini:
  1. Apabila kaedah mempunyai nilai pulangan — if (returningValue! = Null) {
  2. Apabila tiada nilai pulangan — else {
@AfterThrowing(value = "methodExecuting()", throwing = "exception") ialah nasihat yang akan dicetuskan sekiranya berlaku ralat, iaitu apabila kaedah membuang pengecualian. Oleh itu, dengan menjalankan main , kita akan mendapat sejenis pengelogan berasaskan konsol:
Perlaksanaan yang berjaya: kaedah — setValue, kelas — Utama Perlaksanaan yang berjaya: kaedah — getValue, kelas — Utama, nilai pulangan — <sesetengah nilai> Pengecualian dilemparkan: kaedah — checkValue, kelas — Pengecualian utama — java.lang.Pengecualian Pengecualian dilemparkan: kaedah — utama, kelas — Utama, pengecualian — java.lang.Exception
Dan kerana kami tidak mengendalikan pengecualian, kami masih akan mendapat jejak tindanan: Apakah AOP?  Prinsip pengaturcaraan berorientasikan aspek - 3Anda boleh membaca tentang pengecualian dan pengendalian pengecualian dalam artikel ini: Pengecualian dalam Java dan Pengecualian: menangkap dan mengendalikan . Itu sahaja untuk saya hari ini. Hari ini kami berkenalan dengan AOP , dan anda dapat melihat bahawa binatang ini tidak semenakutkan seperti yang disangkakan oleh sesetengah orang. Selamat tinggal semua!
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION