CDI Interceptor vs Spring AspectJ

1. Pengenalan

Corak Interceptor umumnya digunakan untuk menambahkan fungsi atau logik rentas baru dalam aplikasi, dan mempunyai sokongan padu di sebilangan besar perpustakaan.

Dalam artikel ini, kita akan membahas dan membezakan dua perpustakaan utama ini: pemintas CDI dan Spring AspectJ.

2. Persediaan Projek Interceptor CDI

CDI secara resmi didukung untuk Jakarta EE tetapi beberapa implementasi memberikan dukungan untuk menggunakan CDI di lingkungan Java SE. Weld dapat dianggap sebagai salah satu contoh pelaksanaan CDI yang didukung di Java SE.

Untuk menggunakan CDI, kita perlu mengimport perpustakaan Weld di POM kita:

 org.jboss.weld.se weld-se-core 3.0.5.Final 

Perpustakaan Weld terbaru boleh didapati di repositori Maven.

Sekarang mari kita buat pemintas yang mudah.

3. Memperkenalkan CDI Interceptor

Untuk menetapkan kelas yang perlu kita hadapi, mari buat ikatan pemintas:

@InterceptorBinding @Target( { METHOD, TYPE } ) @Retention( RUNTIME ) public @interface Audited { }

Setelah kita menentukan pengikat pemintas, kita perlu menentukan pelaksanaan pemintas yang sebenarnya:

@Audited @Interceptor public class AuditedInterceptor { public static boolean calledBefore = false; public static boolean calledAfter = false; @AroundInvoke public Object auditMethod(InvocationContext ctx) throws Exception { calledBefore = true; Object result = ctx.proceed(); calledAfter = true; return result; } }

Setiap kaedah @AroundInvoke mengambil argumen javax.interceptor.InvocationContext , mengembalikan java.lang.Object , dan boleh membuang Pengecualian .

Oleh itu, apabila kami memberi penjelasan tentang kaedah dengan antara muka @Audit yang baru , auditMode akan dipanggil terlebih dahulu, dan hanya kemudian kaedah sasaran juga akan diteruskan.

4. Sapukan CDI Interceptor

Mari gunakan pemintas yang dibuat pada beberapa logik perniagaan:

public class SuperService { @Audited public String deliverService(String uid) { return uid; } }

Kami telah membuat perkhidmatan mudah ini dan memberi penjelasan kaedah yang kami mahu memintas dengan anotasi @Audited .

Untuk mengaktifkan pemintas CDI, seseorang perlu menentukan nama kelas penuh dalam fail beans.xml , yang terletak di direktori META-INF :

  com.baeldung.interceptor.AuditedInterceptor  

Untuk mengesahkan bahawa pemintas itu telah berjaya, mari kita jalankan ujian berikut :

public class TestInterceptor { Weld weld; WeldContainer container; @Before public void init() { weld = new Weld(); container = weld.initialize(); } @After public void shutdown() { weld.shutdown(); } @Test public void givenTheService_whenMethodAndInterceptorExecuted_thenOK() { SuperService superService = container.select(SuperService.class).get(); String code = "123456"; superService.deliverService(code); Assert.assertTrue(AuditedInterceptor.calledBefore); Assert.assertTrue(AuditedInterceptor.calledAfter); } }

Dalam ujian pantas ini, pertama-tama kami mendapatkan biji SuperService dari bekas, kemudian memanggil kaedah perniagaan menyampaikanService di atasnya dan periksa bahawa pemintas AuditedInterceptor sebenarnya dipanggil dengan mengesahkan pemboleh ubah keadaannya.

Kami juga mempunyai kaedah @Sebelum dan @Setelah dianotasi di mana kami memulakan dan mematikan weld Weld masing-masing.

5. Pertimbangan CDI

Kami dapat menunjukkan kelebihan berikut dari pemintas CDI:

  • Ini adalah ciri standard spesifikasi EE Jakarta
  • Beberapa perpustakaan implementasi CDI dapat digunakan di Java SE
  • Boleh digunakan apabila projek kami mempunyai batasan teruk pada perpustakaan pihak ketiga

Kelemahan pemintas CDI adalah seperti berikut:

  • Gandingan erat antara kelas dengan logik perniagaan dan pemintas
  • Sukar untuk melihat kelas mana yang dipintas dalam projek ini
  • Kekurangan mekanisme fleksibel untuk menerapkan pemintas pada sekumpulan kaedah

6. Spring AspectJ

Spring menyokong pelaksanaan fungsi pemintas yang serupa menggunakan sintaks AspectJ juga.

Mula-mula kita perlu menambahkan pergantungan Spring dan AspectJ berikut ke POM:

 org.springframework spring-context 5.2.8.RELEASE   org.aspectj aspectjweaver 1.9.2 

Versi konteks Spring terkini, aspekjweaver boleh didapati di repositori Maven.

Kita sekarang boleh membuat aspek mudah menggunakan sintaks anotasi AspectJ:

@Aspect public class SpringTestAspect { @Autowired private List accumulator; @Around("execution(* com.baeldung.spring.service.SpringSuperService.*(..))") public Object auditMethod(ProceedingJoinPoint jp) throws Throwable { String methodName = jp.getSignature().getName(); accumulator.add("Call to " + methodName); Object obj = jp.proceed(); accumulator.add("Method called successfully: " + methodName); return obj; } }

Kami mencipta aspek yang berlaku untuk semua kaedah kelas SpringSuperService - yang, untuk kesederhanaan, kelihatan seperti ini:

public class SpringSuperService { public String getInfoFromService(String code) { return code; } }

7. Aspek Spring AspectJ Berlaku

Untuk mengesahkan aspek tersebut benar-benar berlaku untuk perkhidmatan, mari kita tulis ujian unit berikut:

@RunWith(SpringRunner.class) @ContextConfiguration(classes = { AppConfig.class }) public class TestSpringInterceptor { @Autowired SpringSuperService springSuperService; @Autowired private List accumulator; @Test public void givenService_whenServiceAndAspectExecuted_thenOk() { String code = "123456"; String result = springSuperService.getInfoFromService(code); Assert.assertThat(accumulator.size(), is(2)); Assert.assertThat(accumulator.get(0), is("Call to getInfoFromService")); Assert.assertThat(accumulator.get(1), is("Method called successfully: getInfoFromService")); } }

Dalam ujian ini kami menyuntik perkhidmatan kami, memanggil kaedah dan memeriksa hasilnya.

Inilah rupa konfigurasi:

@Configuration @EnableAspectJAutoProxy public class AppConfig { @Bean public SpringSuperService springSuperService() { return new SpringSuperService(); } @Bean public SpringTestAspect springTestAspect() { return new SpringTestAspect(); } @Bean public List getAccumulator() { return new ArrayList(); } }

One important aspect here in the @EnableAspectJAutoProxy annotation – which enables support for handling components marked with AspectJ's @Aspect annotation, similar to functionality found in Spring's XML element.

8. Spring AspectJ Considerations

Let's point out a few of the advantages of using Spring AspectJ:

  • Interceptors are decoupled from the business logic
  • Interceptors can benefit from dependency injection
  • Interceptor has all the configuration information in itself
  • Adding new interceptors wouldn't require augmenting existing code
  • Interceptor has flexible mechanism to choose which methods to intercept
  • Can be used without Jakarta EE

And of course a few of the disadvantages:

  • You need to know the AspectJ syntax to develop interceptors
  • Keluk Pembelajaran bagi pemintas AspectJ lebih tinggi berbanding dengan pemintas CDI

9. CDI Interceptor vs Spring AspectJ

Sekiranya projek semasa anda menggunakan Spring, maka pertimbangkan Spring AspectJ adalah pilihan yang baik.

Sekiranya anda menggunakan pelayan aplikasi sepenuhnya, atau projek anda tidak menggunakan Spring (atau kerangka lain seperti Google Guice) dan tegas Jakarta EE maka tidak ada yang tersisa daripada memilih pemintas CDI.

10. Kesimpulannya

Dalam artikel ini kita telah membahas dua pelaksanaan corak interceptor: interceptor CDI dan Spring AspectJ. Kami telah merangkumi kelebihan dan kekurangan masing-masing.

Kod sumber untuk contoh artikel ini boleh didapati di repositori kami di GitHub.