Pengenalan Model Pemberitahuan Acara dalam CDI 2.0

1. Gambaran keseluruhan

CDI (Contexts and Dependency Injection) adalah kerangka suntikan ketergantungan standard dari platform EE Jakarta.

Dalam tutorial ini, kita akan melihat CDI 2.0 dan bagaimana ia membina mekanisme suntikan CDI 1.x yang kuat dan selamat dengan menambahkan model pemberitahuan acara yang lebih baik dan lengkap.

2. Pergantungan Maven

Untuk memulakan, kami akan membina projek Maven yang ringkas.

Kami memerlukan wadah yang mematuhi CDI 2.0, dan Weld, rujukan pelaksanaan CDI, sangat sesuai:

  javax.enterprise cdi-api 2.0.SP1   org.jboss.weld.se weld-se-core 3.0.5.Final   

Seperti biasa, kami dapat menarik versi terbaru cdi-api dan weld-se-core dari Maven Central.

3. Memerhati dan Mengendalikan Peristiwa Adat

Ringkasnya, model pemberitahuan acara CDI 2.0 adalah implementasi klasik dari pola Observer , berdasarkan anotasi parameter-kaedah @Observes . Oleh itu, ini memungkinkan kita untuk menentukan kaedah pemerhati dengan mudah, yang boleh dipanggil secara automatik sebagai tindak balas terhadap satu atau lebih peristiwa.

Sebagai contoh, kita dapat menentukan satu atau lebih kacang, yang akan mencetuskan satu atau lebih peristiwa tertentu, sementara kacang lain akan diberitahu mengenai peristiwa tersebut dan akan bertindak balas dengan sewajarnya.

Untuk menunjukkan dengan lebih jelas bagaimana ini berfungsi, kami akan membina contoh mudah, termasuk kelas perkhidmatan asas, kelas acara khusus, dan kaedah pemerhati yang bertindak balas terhadap peristiwa kebiasaan kami.

3.1. Kelas Perkhidmatan Asas

Mari mulakan dengan membuat kelas TextService ringkas :

public class TextService { public String parseText(String text) { return text.toUpperCase(); } } 

3.2. Kelas Acara Tersuai

Seterusnya, mari kita tentukan contoh kelas peristiwa, yang mengambil argumen String dalam pembentuknya:

public class ExampleEvent { private final String eventMessage; public ExampleEvent(String eventMessage) { this.eventMessage = eventMessage; } // getter }

3.3. Menentukan Kaedah Pemerhati Dengan Anotasi @Observes

Setelah menentukan kelas perkhidmatan dan acara kami, mari gunakan anotasi @Observes untuk membuat kaedah pemerhati untuk kelas ContohEvent kami :

public class ExampleEventObserver { public String onEvent(@Observes ExampleEvent event, TextService textService) { return textService.parseText(event.getEventMessage()); } }

Walaupun pada pandangan pertama, pelaksanaan kaedah onEvent () kelihatan agak remeh, ia sebenarnya merangkumi banyak fungsi melalui anotasi @Observes .

Seperti yang kita dapat lihat, onEvent () kaedah adalah pengendali peristiwa yang mengambil ExampleEvent dan TextService objek sebagai argumen.

Ingatlah bahawa semua hujah yang dinyatakan selepas penjelasan @Observes adalah titik suntikan standard. Hasilnya, CDI akan membuat contoh yang diinisialisasi sepenuhnya untuk kami dan memasukkannya ke dalam kaedah pemerhati.

3.4. Memulakan Kontena CDI 2.0 Kami

Pada ketika ini, kami telah membuat kelas perkhidmatan dan acara kami, dan kami telah menentukan kaedah pemerhati yang mudah untuk bertindak balas terhadap acara kami. Tetapi bagaimana kita memerintahkan CDI untuk menyuntikkan kejadian ini pada waktu runtime?

Di sinilah model pemberitahuan acara menunjukkan fungsinya sepenuhnya. Kami hanya memulakan pelaksanaan SeContainer baru dan jalankan satu atau lebih acara melalui kaedah fireEvent () :

SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance(); try (SeContainer container = containerInitializer.initialize()) { container.getBeanManager().fireEvent(new ExampleEvent("Welcome to Baeldung!")); }

Perhatikan bahawa kita menggunakan SeContainerInitializer dan SeContainer objek kerana kita menggunakan CDI dalam persekitaran yang Java SE, dan bukannya di Jakarta EE.

Semua kaedah pemerhati yang dilampirkan akan diberitahu apabila ContohEvent dipecat dengan menyebarkan peristiwa itu sendiri.

Oleh kerana semua objek dilampirkan sebagai argumen setelah penjelasan @Observes akan diinisialisasi sepenuhnya, CDI akan menguruskan penyusunan keseluruhan grafik objek TextService untuk kami, sebelum memasukkannya ke dalam kaedah onEvent () .

Ringkasnya, kami mempunyai kelebihan wadah IoC yang selamat untuk jenis, bersama dengan model pemberitahuan acara yang kaya dengan ciri .

4. Acara Permulaan Kontena

Dalam contoh sebelumnya, kami menggunakan peristiwa khusus untuk menyampaikan peristiwa ke kaedah pemerhati dan mendapatkan objek TextService yang diinisialisasi sepenuhnya .

Sudah tentu, ini berguna apabila kita benar-benar perlu menyebarkan satu atau lebih peristiwa di beberapa titik aplikasi kita.

Kadang-kadang, kita hanya perlu mendapatkan sekumpulan objek yang sepenuhnya diinisialisasi yang siap digunakan dalam kelas aplikasi kita , tanpa perlu melalui pelaksanaan acara tambahan.

Untuk tujuan ini, CDI 2.0 menyediakan kelas acara ContainerInitialized , yang secara automatik dipecat ketika weld Weld diinisialisasi .

Mari lihat bagaimana kita boleh menggunakan acara ContainerInitialized untuk memindahkan kawalan ke kelas ContohEventObserver :

public class ExampleEventObserver { public String onEvent(@Observes ContainerInitialized event, TextService textService) { return textService.parseText(event.getEventMessage()); } } 

Dan perlu diingat bahawa yang ContainerInitialized Kelas Peristiwa adalah Weld khusus . Oleh itu, kita perlu memfaktorkan semula kaedah pemerhati kita jika kita menggunakan pelaksanaan CDI yang berbeza.

5. Kaedah Pemerhati Bersyarat

Dalam pelaksanaannya sekarang, kelas ExampleEventObserver kami menentukan kaedah pemerhati tanpa syarat secara lalai. Ini bermaksud bahawa kaedah pemerhati akan selalu diberitahu mengenai peristiwa yang disediakan , tidak kira sama ada contoh kelas wujud dalam konteks semasa.

Likewise, we can define a conditional observer method by specifying notifyObserver=IF_EXISTS as an argument to the @Observes annotation:

public String onEvent(@Observes(notifyObserver=IF_EXISTS) ExampleEvent event, TextService textService) { return textService.parseText(event.getEventMessage()); } 

When we use a conditional observer method, the method will be notified of the matching event only if an instance of the class that defines the observer method exists in the current context.

6. Transactional Observer Methods

We can also fire events within a transaction, such as a database update or removal operation. To do so, we can define transactional observer methods by adding the during argument to the @Observes annotation.

Each possible value of the during argument corresponds to a particular phase of a transaction:

  • BEFORE_COMPLETION
  • AFTER_COMPLETION
  • AFTER_SUCCESS
  • AFTER_FAILURE

If we fire the ExampleEvent event within a transaction, we need to refactor the onEvent() method accordingly to handle the event during the required phase:

public String onEvent(@Observes(during=AFTER_COMPLETION) ExampleEvent event, TextService textService) { return textService.parseText(event.getEventMessage()); }

A transactional observer method will be notified of the supplied event only in the matching phase of a given transaction.

7. Observer Methods Ordering

Another nice improvement included in CDI 2.0's event notification model is the ability for setting up an ordering or priority for calling observers of a given event.

We can easily define the order in which the observer methods will be called by specifying the @Priority annotation after @Observes.

To understand how this feature works, let's define another observer method, aside from the one that ExampleEventObserver implements:

public class AnotherExampleEventObserver { public String onEvent(@Observes ExampleEvent event) { return event.getEventMessage(); } }

In this case, both observer methods by default will have the same priority. Thus, the order in which CDI will invoke them is simply unpredictable.

We can easily fix this by assigning to each method an invocation priority through the @Priority annotation:

public String onEvent(@Observes @Priority(1) ExampleEvent event, TextService textService) { // ... implementation } 
public String onEvent(@Observes @Priority(2) ExampleEvent event) { // ... implementation }

Priority levels follow a natural ordering. Therefore, CDI will call first the observer method with a priority level of 1 and will invoke second the method with a priority level of 2.

Likewise, if we use the same priority level across two or more methods, the order is again undefined.

8. Asynchronous Events

In all the examples that we've learned so far, we fired events synchronously. However, CDI 2.0 allows us to easily fire asynchronous events as well. Asynchronous observer methods can then handle these asynchronous events in different threads.

We can fire an event asynchronously with the fireAsync() method:

public class ExampleEventSource { @Inject Event exampleEvent; public void fireEvent() { exampleEvent.fireAsync(new ExampleEvent("Welcome to Baeldung!")); } }

Beans menghidupkan acara, yang merupakan implementasi antara muka Event . Oleh itu, kita boleh menyuntiknya seperti kacang konvensional yang lain .

Untuk menangani peristiwa tak segerak kita, kita perlu menentukan satu atau lebih kaedah pemerhati tak segerak dengan anotasi @ObservesAsync :

public class AsynchronousExampleEventObserver { public void onEvent(@ObservesAsync ExampleEvent event) { // ... implementation } }

9. Kesimpulannya

Dalam artikel ini, kami belajar bagaimana memulai menggunakan model pemberitahuan acara yang ditingkatkan yang dibundel dengan CDI 2.0.

Seperti biasa, semua contoh kod yang ditunjukkan dalam tutorial ini terdapat di GitHub.