CodeGym /Java Blog /Acak /Apa itu AOP? Prinsip pemrograman berorientasi aspek
John Squirrels
Level 41
San Francisco

Apa itu AOP? Prinsip pemrograman berorientasi aspek

Dipublikasikan di grup Acak
Hai, teman-teman! Tanpa memahami konsep dasar, cukup sulit untuk mempelajari kerangka kerja dan pendekatan untuk membangun fungsionalitas. Jadi hari ini kita akan berbicara tentang satu konsep seperti itu — AOP, alias pemrograman berorientasi aspek . Apa itu AOP?  Prinsip pemrograman berorientasi aspek - 1Topik ini tidak mudah dan jarang digunakan secara langsung, tetapi banyak kerangka kerja dan teknologi yang menggunakannya secara tersembunyi. Dan tentu saja, terkadang selama wawancara, Anda mungkin diminta untuk menjelaskan secara umum jenis binatang apa ini dan di mana itu dapat diterapkan. Jadi mari kita lihat konsep dasar dan beberapa contoh sederhana AOP di Java . Nah, AOP adalah singkatan dari pemrograman berorientasi aspek, yang merupakan paradigma yang dimaksudkan untuk meningkatkan modularitas dari berbagai bagian aplikasi dengan memisahkan masalah lintas sektoral. Untuk melakukannya, perilaku tambahan ditambahkan ke kode yang sudah ada tanpa membuat perubahan pada kode aslinya. Dengan kata lain, kita dapat menganggapnya sebagai menggantung fungsionalitas tambahan di atas metode dan kelas tanpa mengubah kode yang dimodifikasi. Mengapa ini perlu? Cepat atau lambat, kami menyimpulkan bahwa pendekatan berorientasi objek yang khas tidak selalu dapat menyelesaikan masalah tertentu secara efektif. Dan ketika saat itu tiba, AOP datang untuk menyelamatkan dan memberi kami alat tambahan untuk membuat aplikasi. Dan alat tambahan berarti peningkatan fleksibilitas dalam pengembangan perangkat lunak, yang berarti lebih banyak pilihan untuk memecahkan masalah tertentu.

Menerapkan AOP

Pemrograman berorientasi aspek dirancang untuk melakukan tugas lintas sektoral, yang dapat berupa kode apa pun yang dapat diulang berkali-kali dengan metode berbeda, yang tidak dapat sepenuhnya disusun menjadi modul terpisah. Karenanya, AOP memungkinkan kita menyimpan ini di luar kode utama dan mendeklarasikannya secara vertikal. Contohnya adalah menggunakan kebijakan keamanan dalam aplikasi. Biasanya, keamanan dijalankan melalui banyak elemen aplikasi. Terlebih lagi, kebijakan keamanan aplikasi harus diterapkan sama untuk semua bagian aplikasi yang ada dan yang baru. Pada saat yang sama, kebijakan keamanan yang digunakan dapat berkembang dengan sendirinya. Ini adalah tempat yang tepat untuk menggunakan AOP . Juga, contoh lain adalah logging. Ada beberapa keuntungan menggunakan pendekatan AOP untuk logging daripada menambahkan fungsional logging secara manual:
  1. Kode untuk masuk mudah untuk ditambahkan dan dihapus: yang perlu Anda lakukan hanyalah menambah atau menghapus beberapa konfigurasi dari beberapa aspek.

  2. Semua kode sumber untuk logging disimpan di satu tempat, jadi Anda tidak perlu mencari secara manual semua tempat yang digunakan.

  3. Kode logging dapat ditambahkan di mana saja, baik dalam metode dan kelas yang sudah ditulis atau dalam fungsionalitas baru. Ini mengurangi jumlah kesalahan pengkodean.

    Selain itu, saat menghapus aspek dari konfigurasi desain, Anda dapat yakin bahwa semua kode penelusuran hilang dan tidak ada yang terlewatkan.

  4. Aspek adalah kode terpisah yang dapat diperbaiki dan digunakan berulang kali.
Apa itu AOP?  Prinsip pemrograman berorientasi aspek - 2AOP juga digunakan untuk penanganan pengecualian, caching, dan mengekstrak fungsionalitas tertentu agar dapat digunakan kembali.

Prinsip dasar AOP

Untuk melangkah lebih jauh dalam topik ini, pertama mari kita mengenal konsep utama AOP. Nasihat — Logika atau kode tambahan yang dipanggil dari titik gabungan. Nasihat dapat dilakukan sebelum, sesudah, atau sebagai pengganti titik bergabung (lebih lanjut tentang mereka di bawah). Jenis saran yang mungkin :
  1. Sebelum — saran jenis ini diluncurkan sebelum metode target, yaitu poin gabungan, dijalankan. Saat menggunakan aspek sebagai kelas, kami menggunakan anotasi @Before untuk menandai saran yang akan datang sebelumnya. Saat menggunakan aspek sebagai file .aj , ini akan menjadi metode before() .

  2. After — saran yang dijalankan setelah eksekusi metode (gabung poin) selesai, baik dalam eksekusi normal maupun saat melempar pengecualian.

    Saat menggunakan aspek sebagai kelas, kita dapat menggunakan anotasi @After untuk menunjukkan bahwa ini adalah saran yang muncul setelahnya.

    Saat menggunakan aspek sebagai file .aj , ini adalah metode after() .

  3. After Returning — saran ini dilakukan hanya ketika metode target selesai secara normal, tanpa kesalahan.

    Saat aspek direpresentasikan sebagai kelas, kita dapat menggunakan anotasi @AfterReturning untuk menandai saran sebagai eksekusi setelah berhasil diselesaikan.

    Saat menggunakan aspek sebagai file .aj , ini akan menjadi metode after() yang mengembalikan (Object obj) .

  4. After Throwing — saran ini dimaksudkan untuk contoh ketika sebuah metode, yaitu, join point, melontarkan pengecualian. Kami dapat menggunakan saran ini untuk menangani beberapa jenis eksekusi yang gagal (misalnya, untuk memutar kembali seluruh transaksi atau mencatat dengan tingkat jejak yang diperlukan).

    Untuk aspek kelas, anotasi @AfterThrowing digunakan untuk menunjukkan bahwa saran ini digunakan setelah melontarkan pengecualian.

    Saat menggunakan aspek sebagai file .aj , ini akan menjadi metode after() throw (Exception e) .

  5. Sekitar — mungkin salah satu jenis nasihat terpenting. Itu mengelilingi metode, yaitu, titik gabungan yang dapat kita gunakan untuk, misalnya, memilih apakah akan melakukan metode titik gabungan yang diberikan atau tidak.

    Anda dapat menulis kode saran yang dijalankan sebelum dan sesudah metode join point dijalankan.

    The around advice bertanggung jawab untuk memanggil metode join point dan mengembalikan nilai jika metode tersebut mengembalikan sesuatu. Dengan kata lain, dalam saran ini, Anda cukup mensimulasikan pengoperasian metode target tanpa memanggilnya, dan mengembalikan apa pun yang Anda inginkan sebagai hasil pengembalian.

    Mengingat aspek sebagai kelas, kami menggunakan anotasi @Around untuk membuat saran yang menggabungkan titik gabung. Saat menggunakan aspek dalam bentuk file .aj , metode ini akan menjadi metode around() .

Join Point — titik dalam program yang sedang berjalan (yaitu pemanggilan metode, pembuatan objek, akses variabel) di mana saran harus diterapkan. Dengan kata lain, ini adalah sejenis ekspresi reguler yang digunakan untuk menemukan tempat injeksi kode (tempat saran harus diterapkan). Pointcut — satu set poin gabungan . Pointcut menentukan apakah saran yang diberikan berlaku untuk titik gabungan tertentu. Aspect — modul atau kelas yang mengimplementasikan fungsionalitas lintas sektoral. Aspect mengubah perilaku kode yang tersisa dengan menerapkan saran pada titik gabung yang ditentukan oleh beberapa pointcut . Dengan kata lain, ini adalah kombinasi dari saran dan poin gabungan. Perkenalan— mengubah struktur kelas dan/atau mengubah hierarki pewarisan untuk menambahkan fungsionalitas aspek ke kode asing. Target — objek yang akan diterapkan saran tersebut. Weaving — proses menghubungkan aspek ke objek lain untuk membuat objek proxy yang disarankan. Ini dapat dilakukan pada waktu kompilasi, waktu muat, atau waktu berjalan. Ada tiga jenis tenun:
  • Menenun waktu kompilasi — jika Anda memiliki kode sumber aspek dan kode tempat Anda menggunakan aspek, maka Anda dapat mengompilasi kode sumber dan aspek secara langsung menggunakan kompiler AspectJ;

  • Anyaman pasca-kompilasi (anyaman biner) — jika Anda tidak dapat atau tidak ingin menggunakan transformasi kode sumber untuk merangkai aspek ke dalam kode, Anda dapat mengambil kelas atau file jar yang telah dikompilasi sebelumnya dan menyuntikkan aspek ke dalamnya;

  • Anyaman waktu muat — ini hanya anyaman biner yang ditunda hingga pemuat kelas memuat file kelas dan menentukan kelas untuk JVM.

    Satu atau lebih pemuat kelas tenun diperlukan untuk mendukung ini. Mereka disediakan secara eksplisit oleh runtime atau diaktifkan oleh "agen penenun".

AspectJ — Implementasi khusus dari paradigma AOP yang mengimplementasikan kemampuan untuk melakukan tugas lintas sektoral. Dokumentasi dapat ditemukan di sini .

Contoh di Jawa

Selanjutnya, untuk pemahaman yang lebih baik tentang AOP , kita akan melihat contoh kecil bergaya "Hello World". Langsung saja, saya perhatikan bahwa contoh kita akan menggunakan waktu kompilasi tenun . Pertama, kita perlu menambahkan dependensi berikut di file pom.xml kita :

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.9.5</version>
</dependency>
Sebagai aturan, kompiler ajc khusus adalah cara kami menggunakan aspek. IntelliJ IDEA tidak menyertakannya secara default, jadi saat memilihnya sebagai kompiler aplikasi, Anda harus menentukan jalur ke distribusi 5168 75 AspectJ . Ini adalah cara pertama. Yang kedua, yang saya gunakan, adalah mendaftarkan plugin berikut di file 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>
Setelah ini, sebaiknya impor ulang dari Maven dan jalankan mvn clean compile . Sekarang mari kita lanjutkan langsung ke contoh.

Contoh No.1

Mari kita buat kelas Utama . Di dalamnya, kita akan memiliki titik masuk dan metode yang mencetak nama yang diteruskan di 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 memberikan nama dan menampilkannya di konsol. Jika kita menjalankan program sekarang, kita akan melihat yang berikut di konsol:
Penyamak Victor Sasha
Nah, sekarang saatnya memanfaatkan kekuatan AOP. Sekarang kita perlu membuat file aspek . Ada dua jenis: yang pertama memiliki ekstensi file .aj . Yang kedua adalah kelas biasa yang menggunakan anotasi untuk mengimplementasikan kemampuan AOP . Pertama mari kita lihat file dengan ekstensi .aj :

public aspect GreetingAspect {
 
  pointcut greeting() : execution(* Main.printName(..));
 
  before() : greeting() {
     System.out.print("Hi, ");
  }
}
File ini agak seperti kelas. Mari kita lihat apa yang terjadi di sini: pointcut adalah sekumpulan titik gabungan; salam() adalah nama dari pointcut ini; : eksekusi menunjukkan untuk menerapkannya selama eksekusi semua ( * ) panggilan metode Main.printName(...) . Selanjutnya muncul saran khusus — sebelum () — yang dijalankan sebelum metode target dipanggil. : greeting() adalah cutpoint yang ditanggapi oleh saran ini. Nah, di bawah ini kita melihat isi dari metode itu sendiri, yang ditulis dalam bahasa Jawa, yang kita pahami. Saat kita menjalankan main dengan aspek ini, kita akan mendapatkan keluaran konsol ini:
Hai, Tanner Hai, Victor Hai, Sasha
Kita dapat melihat bahwa setiap panggilan ke metode printName telah dimodifikasi berkat suatu aspek. Sekarang mari kita lihat seperti apa aspeknya 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, ");
  }
}
Setelah file aspek .aj , semuanya menjadi lebih jelas di sini:
  • @Aspect menunjukkan bahwa kelas ini adalah sebuah aspek;
  • @Pointcut("execution(* Main.printName(String))") adalah cutpoint yang dipicu untuk semua panggilan ke Main.printName dengan argumen input yang bertipe String ;
  • @Before("greeting()") adalah saran yang diterapkan sebelum memanggil kode yang ditentukan dalam titik potong greeting() .
Menjalankan main dengan aspek ini tidak mengubah keluaran konsol:
Hai, Tanner Hai, Victor Hai, Sasha

Contoh No.2

Misalkan kita memiliki beberapa metode yang melakukan beberapa operasi untuk klien, dan kita memanggil metode 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 gunakan anotasi @Around untuk membuat "transaksi semu":

@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 metode proses objek ProceedingJoinPoint , kami memanggil metode pembungkusan untuk menentukan lokasinya di saran. Oleh karena itu, kode dalam metode di atas joinPoint.proceed(); adalah Before , sedangkan kode dibawahnya adalah After . Jika kita menjalankan main , kita mendapatkan ini di konsol:
Membuka transaksi... Melakukan beberapa operasi untuk Klien Tanner Menutup transaksi...
Tetapi jika kita membuang dan pengecualian dalam metode 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 mendapatkan keluaran konsol ini:
Membuka transaksi... Melakukan beberapa operasi untuk Klien Tanner Operasi gagal. Mengembalikan transaksi...
Jadi yang kita dapatkan di sini adalah semacam kemampuan penanganan kesalahan.

Contoh No.3

Dalam contoh kita berikutnya, mari lakukan sesuatu seperti masuk ke konsol. Pertama, lihat Main , di mana kami telah menambahkan beberapa logika bisnis semu:

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();
     }
  }
}
Di main , kami menggunakan setValue untuk menetapkan nilai ke variabel instance nilai . Kemudian kami menggunakan getValue untuk mendapatkan nilainya, dan kemudian kami memanggil checkValue untuk melihat apakah lebih dari 10 karakter. Jika demikian, maka pengecualian akan dilemparkan. Sekarang mari kita lihat aspek yang akan kita gunakan untuk mencatat pekerjaan metode:

@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 terjadi di sini? @Pointcut("execution(* *(..))") akan menggabungkan semua panggilan dari semua metode. @AfterReturning(value = "methodExecuting()", return = "returningValue") adalah saran yang akan dijalankan setelah eksekusi sukses dari metode target. Kami memiliki dua kasus di sini:
  1. Ketika metode memiliki nilai pengembalian — if (returningValue! = Null) {
  2. Ketika tidak ada nilai kembalian — else {
@AfterThrowing(value = "methodExecuting()", throw = "exception") adalah saran yang akan dipicu jika terjadi kesalahan, yaitu saat metode melempar pengecualian. Dan karenanya, dengan menjalankan main , kita akan mendapatkan semacam logging berbasis konsol:
Eksekusi yang berhasil: metode — setValue, kelas — Utama Eksekusi yang berhasil: metode — getValue, kelas — Utama, nilai balik — <nilai tertentu> Pengecualian dilontarkan: metode — checkValue, kelas — Pengecualian utama — java.lang.Exception Pengecualian dilempar: metode — utama, kelas — Utama, pengecualian — java.lang.Exception
Dan karena kami tidak menangani pengecualian, kami masih akan mendapatkan pelacakan tumpukan: Apa itu AOP?  Prinsip pemrograman berorientasi aspek - 3Anda dapat membaca tentang pengecualian dan penanganan pengecualian di artikel ini: Pengecualian di Jawa dan Pengecualian: penangkapan dan penanganan . Itu saja untuk saya hari ini. Hari ini kami berkenalan dengan AOP , dan Anda dapat melihat bahwa binatang buas ini tidak seseram yang dibayangkan beberapa orang. Selamat tinggal semuanya!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION