
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:-
Kod untuk pengelogan mudah untuk ditambah dan dialih keluar: semua yang anda perlu lakukan ialah menambah atau mengalih keluar beberapa konfigurasi beberapa aspek.
-
Semua kod sumber untuk pembalakan disimpan di satu tempat, jadi anda tidak perlu memburu secara manual semua tempat di mana ia digunakan.
-
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.
- Aspek ialah kod berasingan yang boleh diperbaiki dan digunakan berulang kali.

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 :-
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() .
- 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() .
-
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) .
-
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) .
-
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() .
-
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."
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:
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:
@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.
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:
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:
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:
- Apabila kaedah mempunyai nilai pulangan — if (returningValue! = Null) {
- Apabila tiada nilai pulangan — else {

GO TO FULL VERSION