Pemrograman Berorientasi Aspek, Pegas AOP

Aspect-oriented programming (AOP) adalah paradigma pemrograman yang merupakan pengembangan lebih lanjut dari pemrograman prosedural dan berorientasi objek (OOP). Gagasan AOP adalah untuk menyoroti apa yang disebut fungsionalitas ujung ke ujung. Dan semuanya sudah beres, di sini saya akan menunjukkan bagaimana melakukannya di Java - Spring @AspectJ annotation style (ada juga gaya xml berbasis skema, fungsinya mirip).

Menyorot fungsionalitas ujung ke ujung


Untuk

gambar

dan sesudahnya

gambar

Yaitu ada fungsi yang mempengaruhi beberapa modul, tetapi tidak secara langsung berhubungan dengan kode bisnis, dan alangkah baiknya untuk meletakkannya di tempat yang terpisah, ini ditunjukkan pada gambar di atas.

Gabung titik



gambar

Join point - konsep AOP berikutnya, ini adalah titik pengamatan, aksesi ke kode tempat pengenalan fungsi direncanakan.

Pangkas


gambar

Potongan titik adalah irisan, kueri untuk poin lampiran, itu bisa satu atau lebih poin. Aturan untuk meminta poin sangat beragam, pada gambar di atas, permintaan untuk penjelasan tentang suatu metode dan metode tertentu. Aturan dapat digabungkan dengan &&, ||,!

Saran


gambar

Saran - seperangkat instruksi yang dieksekusi pada titik potong (Pointcut). Instruksi dapat dilakukan pada acara dengan berbagai jenis:

  • Sebelum - sebelum panggilan metode
  • Setelah - setelah panggilan metode
  • Setelah kembali - setelah mengembalikan nilai dari suatu fungsi
  • Setelah melempar - dalam kasus pengecualian
  • Setelah akhirnya - jika akhirnya blok dijalankan
  • Sekitar - Anda dapat melakukan pre., Post., Memproses sebelum pemanggilan metode, dan juga secara umum mem-bypass pemanggilan metode.

pada satu Pointcut Anda dapat "menggantung" beberapa Saran dari berbagai jenis.

Aspek


gambar

Aspect - modul yang berisi deskripsi Pointcut dan Saran.

Sekarang saya akan memberikan contoh dan akhirnya semuanya akan jatuh (atau hampir semua) pada tempatnya. Kita semua tahu tentang kode logging yang menyerap banyak modul, tidak terkait dengan kode bisnis, tetapi Anda tidak bisa. Jadi saya memisahkan fungsi ini dari kode bisnis.
Contoh - pencatatan kode

Layanan target

@Service public class MyService { public void method1(List<String> list) { list.add("method1"); System.out.println("MyService method1 list.size=" + list.size()); } @AspectAnnotation public void method2() { System.out.println("MyService method2"); } public boolean check() { System.out.println("MyService check"); return true; } } 

Sebuah aspek dengan deskripsi Pointcut dan Saran.

 @Aspect @Component public class MyAspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { } @Before("callAtMyServicePublic()") public void beforeCallAtMethod1(JoinPoint jp) { String args = Arrays.stream(jp.getArgs()) .map(a -> a.toString()) .collect(Collectors.joining(",")); logger.info("before " + jp.toString() + ", args=[" + args + "]"); } @After("callAtMyServicePublic()") public void afterCallAt(JoinPoint jp) { logger.info("after " + jp.toString()); } } 

Dan kode uji panggilan

 @RunWith(SpringRunner.class) @SpringBootTest public class DemoAspectsApplicationTests { @Autowired private MyService service; @Test public void testLoggable() { List<String> list = new ArrayList(); list.add("test"); service.method1(list); service.method2(); Assert.assertTrue(service.check()); } } 

Penjelasan Dalam layanan target tidak disebutkan logging, dalam kode panggilan lebih dari itu, semua logging terkonsentrasi dalam modul terpisah
@Aspect
class MyAspect ...


Dalam pointcut

  @Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { } 

Saya meminta semua metode MyService publik dengan tipe pengembalian * dan jumlah argumen (..)

Dalam Saran Sebelum dan Setelah yang merujuk ke Pointcut (callAtMyServicePublic) , saya menulis instruksi untuk menulis ke log. JoinPoint bukan parameter yang diperlukan, yang menyediakan informasi tambahan, tetapi jika digunakan, maka itu harus menjadi yang pertama.
Semuanya ditempatkan dalam modul yang berbeda! Kode penelepon, target, logging.

Menghasilkan konsol

gambar

Aturan pemotongan poin dapat bervariasi
Beberapa contoh Pointcut dan Saran:

Meminta anotasi tentang suatu metode.

 @Pointcut("@annotation(AspectAnnotation)") public void callAtMyServiceAnnotation() { } 

Nasihat untuknya

  @Before("callAtMyServiceAnnotation()") public void beforeCallAt() { } 

Permintaan metode tertentu yang menunjukkan parameter metode target

 @Pointcut("execution(* com.example.demoAspects.MyService.method1(..)) && args(list,..))") public void callAtMyServiceMethod1(List<String> list) { } 

Nasihat untuknya

  @Before("callAtMyServiceMethod1(list)") public void beforeCallAtMethod1(List<String> list) { } 

Potong untuk hasil pengembalian

  @Pointcut("execution(* com.example.demoAspects.MyService.check())") public void callAtMyServiceAfterReturning() { } 

Nasihat untuknya

  @AfterReturning(pointcut="callAtMyServiceAfterReturning()", returning="retVal") public void afterReturningCallAt(boolean retVal) { } 

Contoh memeriksa hak untuk Saran tipe Sekitar, melalui anotasi

 @Retention(RUNTIME) @Target(METHOD) public @interface SecurityAnnotation { } // @Aspect @Component public class MyAspect { @Pointcut("@annotation(SecurityAnnotation) && args(user,..)") public void callAtMyServiceSecurityAnnotation(User user) { } @Around("callAtMyServiceSecurityAnnotation(user)") public Object aroundCallAt(ProceedingJoinPoint pjp, User user) { Object retVal = null; if (securityService.checkRight(user)) { retVal = pjp.proceed(); } return retVal; } 

Metode yang perlu diperiksa sebelum panggilan, ke kanan, dapat dijelaskan dengan "SecurityAnnotation", maka di Aspect kita akan mendapatkan sepotong dari mereka, dan semuanya akan dicegat sebelum panggilan dan pemeriksaan hak akan dilakukan.

Kode Target:

 @Service public class MyService { @SecurityAnnotation public Balance getAccountBalance(User user) { // ... } @SecurityAnnotation public List<Transaction> getAccountTransactions(User user, Date date) { // ... } } 

Kode Penelepon:

 balance = myService.getAccountBalance(user); if (balance == null) { accessDenied(user); } else { displayBalance(balance); } 

Yaitu dalam kode panggilan dan target, tidak ada verifikasi hak, hanya kode bisnis itu sendiri.
Contoh profil layanan yang sama menggunakan Saran tipe Sekitar

 @Aspect @Component public class MyAspect { @Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { } @Around("callAtMyServicePublic()") public Object aroundCallAt(ProceedingJoinPoint call) throws Throwable { StopWatch clock = new StopWatch(call.toString()); try { clock.start(call.toShortString()); return call.proceed(); } finally { clock.stop(); System.out.println(clock.prettyPrint()); } } } 

Jika kita menjalankan kode panggilan dengan panggilan ke metode MyService, kita mendapatkan waktu untuk memanggil setiap metode. Jadi, tanpa mengubah kode panggilan dan target, saya menambahkan fitur baru: logging, profiler dan keamanan.
Contoh penggunaan dalam formulir UI

Ada kode yang dengan menyetel menyembunyikan / menampilkan bidang pada formulir:

 public class EditForm extends Form { @Override public void init(Form form) { formHelper.updateVisibility(form, settingsService.isVisible(COMP_NAME)); formHelper.updateVisibility(form, settingsService.isVisible(COMP_LAST_NAME)); formHelper.updateVisibility(form, settingsService.isVisible(COMP_BIRTH_DATE)); // ... } 

Anda juga dapat menghapus pembaruanVisibilitas dalam Saran tipe Sekitar

 @Aspect public class MyAspect { @Pointcut("execution(* com.example.demoAspects.EditForm.init() && args(form,..))") public void callAtInit(Form form) { } // ... @Around("callAtInit(form)") public Object aroundCallAt(ProceedingJoinPoint pjp, Form form) { formHelper.updateVisibility(form, settingsService.isVisible(COMP_NAME)); formHelper.updateVisibility(form, settingsService.isVisible(COMP_LAST_NAME)); formHelper.updateVisibility(form, settingsService.isVisible(COMP_BIRTH_DATE)); Object retVal = pjp.proceed(); return retVal; } 

dll.

Struktur proyek

gambar

file pom
 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demoAspects</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demoAspects</name> <description>Demo project for Spring Boot Aspects</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 


Material

Pemrograman Berorientasi Aspek dengan Musim Semi

Source: https://habr.com/ru/post/id428548/


All Articles