Acara Musim Semi

1. Ikhtisar

Di artikel ini, kami akan membahas cara menggunakan acara di Musim Semi .

Peristiwa adalah salah satu fungsi yang lebih diabaikan dalam kerangka kerja tetapi juga salah satu yang lebih berguna. Dan - seperti banyak hal lain di Spring - penerbitan acara adalah salah satu kemampuan yang disediakan oleh ApplicationContext.

Ada beberapa pedoman sederhana untuk diikuti:

  • acara tersebut harus memperpanjang ApplicationEvent
  • penerbit harus memasukkan objek ApplicationEventPublisher
  • pendengar harus mengimplementasikan antarmuka ApplicationListener

2. Acara Kustom

Spring memungkinkan kami membuat dan memublikasikan acara khusus yang - secara default - sinkron . Ini memiliki beberapa keuntungan - seperti, misalnya, pendengar dapat berpartisipasi dalam konteks transaksi penerbit.

2.1. Acara Aplikasi Sederhana

Mari buat kelas acara sederhana - hanya tempat penampung untuk menyimpan data acara. Dalam kasus ini, kelas acara menyimpan pesan String:

public class CustomSpringEvent extends ApplicationEvent { private String message; public CustomSpringEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } }

2.2. Penerbit

Sekarang mari kita buat penerbit acara itu . Penerbit membuat objek acara dan menerbitkannya kepada siapa saja yang mendengarkan.

Untuk memublikasikan acara, penerbit cukup memasukkan ApplicationEventPublisher dan menggunakan API publishEvent () :

@Component public class CustomSpringEventPublisher { @Autowired private ApplicationEventPublisher applicationEventPublisher; public void publishCustomEvent(final String message) { System.out.println("Publishing custom event. "); CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message); applicationEventPublisher.publishEvent(customSpringEvent); } }

Alternatifnya, kelas penerbit dapat mengimplementasikan antarmuka ApplicationEventPublisherAware - ini juga akan memasukkan penerbit acara pada permulaan aplikasi. Biasanya, lebih mudah untuk hanya menyuntikkan @Autowire ke penerbit .

2.3. Seorang Pendengar

Terakhir, mari buat pendengar.

Satu-satunya persyaratan untuk pendengar adalah menjadi kacang dan mengimplementasikan antarmuka ApplicationListener :

@Component public class CustomSpringEventListener implements ApplicationListener { @Override public void onApplicationEvent(CustomSpringEvent event) { System.out.println("Received spring custom event - " + event.getMessage()); } }

Perhatikan bagaimana pendengar khusus kita diparameterisasi dengan jenis umum kejadian khusus - yang membuat metode onApplicationEvent () aman untuk jenis. Ini juga menghindari keharusan untuk memeriksa apakah objek tersebut adalah turunan dari kelas acara tertentu dan mentransmisikannya.

Dan, seperti yang telah dibahas - secara default peristiwa musim semi adalah sinkron - metode doStuffAndPublishAnEvent () memblokir hingga semua pendengar selesai memproses peristiwa tersebut.

3. Membuat Peristiwa Asinkron

Dalam beberapa kasus, menerbitkan acara secara sinkron bukanlah yang kami cari - kami mungkin perlu penanganan asinkron acara kami .

Anda dapat mengaktifkannya dalam konfigurasi dengan membuat bean ApplicationEventMulticaster dengan eksekutor; untuk tujuan kita di sini SimpleAsyncTaskExecutor bekerja dengan baik:

@Configuration public class AsynchronousSpringEventsConfig { @Bean(name = "applicationEventMulticaster") public ApplicationEventMulticaster simpleApplicationEventMulticaster() { SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); return eventMulticaster; } }

Peristiwa, penerbit, dan implementasi pemroses tetap sama seperti sebelumnya - tetapi sekarang, pemroses akan menangani peristiwa secara asinkron di utas terpisah .

4. Kerangka Acara yang Ada

Spring sendiri menerbitkan berbagai acara di luar kotak. Misalnya, ApplicationContext akan mengaktifkan berbagai peristiwa kerangka kerja. Misalnya ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent, dll.

Peristiwa ini memberi pengembang aplikasi opsi untuk menghubungkan ke siklus hidup aplikasi dan konteks dan menambahkan logika kustom mereka sendiri jika diperlukan.

Berikut adalah contoh cepat pendengar yang mendengarkan penyegaran konteks:

public class ContextRefreshedListener implements ApplicationListener { @Override public void onApplicationEvent(ContextRefreshedEvent cse) { System.out.println("Handling context re-freshed event. "); } }

Untuk mempelajari lebih lanjut tentang kejadian kerangka kerja yang ada, lihat tutorial kami berikutnya di sini.

5. Pemroses Peristiwa Berbasis Anotasi

Dimulai dengan Spring 4.2, pendengar acara tidak diharuskan menjadi kacang yang mengimplementasikan antarmuka ApplicationListener - ia dapat didaftarkan pada metode publik apa pun dari kacang yang dikelola melalui anotasi @EventListener :

@Component public class AnnotationDrivenEventListener { @EventListener public void handleContextStart(ContextStartedEvent cse) { System.out.println("Handling context started event."); } }

Seperti sebelumnya, tanda tangan metode mendeklarasikan jenis peristiwa yang dikonsumsi.

Secara default, listener dipanggil secara sinkron. Namun, kita dapat dengan mudah membuatnya asinkron dengan menambahkan anotasi @Async . Kita harus ingat untuk mengaktifkan dukungan Async dalam aplikasi tersebut.

6. Dukungan Generik

Dimungkinkan juga untuk mengirimkan acara dengan informasi umum dalam jenis acara.

6.1. Acara Aplikasi Generik

Mari buat jenis acara umum . Dalam contoh kami, kelas acara menyimpan konten apa pun dan indikator status sukses :

public class GenericSpringEvent { private T what; protected boolean success; public GenericSpringEvent(T what, boolean success) { this.what = what; this.success = success; } // ... standard getters }

Perhatikan perbedaan antara GenericSpringEvent dan CustomSpringEvent . Kami sekarang memiliki fleksibilitas untuk mempublikasikan acara arbitrer apa pun dan tidak perlu lagi diperpanjang dari ApplicationEvent .

6.2. Seorang Pendengar

Sekarang mari kita buat pendengar acara itu . Kita bisa mendefinisikan pendengar dengan mengimplementasikan antarmuka ApplicationListener seperti sebelumnya:

@Component public class GenericSpringEventListener implements ApplicationListener
    
      { @Override public void onApplicationEvent(@NonNull GenericSpringEvent event) { System.out.println("Received spring generic event - " + event.getWhat()); } }
    

But unfortunately, this definition requires us to inherit GenericSpringEvent from the ApplicationEvent class. So for this tutorial, let's make use of an annotation-driven event listener discussed previously.

It is also possible to make the event listener conditional by defining a boolean SpEL expression on the @EventListener annotation. In this case, the event handler will only be invoked for a successful GenericSpringEvent of String:

@Component public class AnnotationDrivenEventListener { @EventListener(condition = "#event.success") public void handleSuccessful(GenericSpringEvent event) { System.out.println("Handling generic event (conditional)."); } }

The Spring Expression Language (SpEL) is a powerful expression language that's covered in detail in another tutorial.

6.3. A Publisher

The event publisher is similar to the one described above. But due to type erasure, we need to publish an event that resolves the generics parameter we would filter on. For example, class GenericStringSpringEvent extends GenericSpringEvent.

And there's an alternative way of publishing events. If we return a non-null value from a method annotated with @EventListener as the result, Spring Framework will send that result as a new event for us. Moreover, we can publish multiple new events by returning them in a collection as the result of event processing.

7. Transaction Bound Events

This paragraph is about using the @TransactionalEventListener annotation. To learn more about transaction management check out the Transactions with Spring and JPA tutorial.

Since Spring 4.2, the framework provides a new @TransactionalEventListener annotation, which is an extension of @EventListener, that allows binding the listener of an event to a phase of the transaction. Binding is possible to the following transaction phases:

  • AFTER_COMMIT (default) is used to fire the event if the transaction has completed successfully
  • AFTER_ROLLBACK – if the transaction has rolled back
  • AFTER_COMPLETION – if the transaction has completed (an alias for AFTER_COMMIT and AFTER_ROLLBACK)
  • BEFORE_COMMIT is used to fire the event right before transaction commit

Here's a quick example of transactional event listener:

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) public void handleCustom(CustomSpringEvent event) { System.out.println("Handling event inside a transaction BEFORE COMMIT."); }

This listener will be invoked only if there's a transaction in which the event producer is running and it's about to be committed.

And, if no transaction is running the event isn’t sent at all unless we override this by setting fallbackExecution attribute to true.

8. Conclusion

Dalam tutorial singkat ini, kami membahas dasar-dasar menangani acara di Spring - membuat acara khusus sederhana, menerbitkannya, dan kemudian menanganinya di pendengar.

Kami juga telah melihat sekilas tentang cara mengaktifkan pemrosesan peristiwa asinkron di konfigurasi.

Kemudian kami mempelajari tentang peningkatan yang diperkenalkan pada Spring 4.2, seperti listener yang didorong anotasi, dukungan generik yang lebih baik, dan peristiwa yang mengikat fase transaksi.

Seperti biasa, kode yang disajikan dalam artikel ini tersedia di Github. Ini adalah proyek berbasis Maven, jadi semestinya mudah untuk mengimpor dan menjalankan apa adanya.