Acara Musim Bunga

1. Gambaran keseluruhan

Dalam artikel ini, kita akan membincangkan cara menggunakan acara pada musim bunga .

Acara adalah salah satu fungsi yang lebih diabaikan dalam kerangka tetapi juga salah satu yang lebih berguna. Dan - seperti banyak perkara lain di Spring - penerbitan acara adalah salah satu keupayaan yang disediakan oleh ApplicationContext.

Terdapat beberapa panduan mudah untuk diikuti:

  • acara itu harus memanjangkan ApplicationEvent
  • penerbit harus menyuntik objek ApplicationEventPublisher
  • pendengar harus melaksanakan antara muka ApplicationListener

2. Peristiwa Adat

Spring membolehkan kita membuat dan menerbitkan acara khusus yang - secara lalai - segerak . Ini mempunyai beberapa kelebihan - seperti, misalnya, pendengar dapat mengambil bahagian dalam konteks transaksi penerbit.

2.1. Acara Aplikasi Mudah

Mari buat kelas acara sederhana - hanya tempat letak untuk menyimpan data acara. Dalam kes ini, kelas acara memegang mesej 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 buat penerbit acara itu . Penerbit membina objek acara dan menerbitkannya kepada sesiapa sahaja yang mendengar.

Untuk menerbitkan acara, penerbit hanya dapat menyuntikkan 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); } }

Sebagai alternatif, kelas penerbit dapat menerapkan antara muka ApplicationEventPublisherAware - ini juga akan menyuntikkan penerbit acara pada permulaan aplikasi. Biasanya, lebih mudah untuk hanya menyuntik penerbit dengan @Autowire.

2.3. Seorang Pendengar

Akhirnya, mari buat pendengar.

Satu-satunya syarat untuk pendengar adalah menjadi kacang dan melaksanakan antara muka 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 tersuai kami diparetriasikan dengan jenis peristiwa khas umum - yang menjadikan kaedah onApplicationEvent () selamat untuk jenis. Ini juga mengelakkan untuk memeriksa apakah objek itu adalah contoh kelas peristiwa tertentu dan membuangnya.

Dan, seperti yang telah dibincangkan - secara langsung peristiwa musim bunga adalah segerak - kaedah doStuffAndPublishAnEvent () menyekat sehingga semua pendengar selesai memproses acara tersebut.

3. Membuat Acara Tidak segerak

Dalam beberapa kes, penerbitan acara secara serentak bukanlah yang sebenarnya kita cari - kita mungkin memerlukan pengendalian acara kita secara tidak segerak .

Anda boleh menghidupkannya dalam konfigurasi dengan membuat kacang ApplicationEventMulticaster dengan pelaksana; untuk tujuan kami di sini SimpleAsyncTaskExecutor berfungsi dengan baik:

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

Acara, penerbit, dan implementasi pendengar tetap sama seperti sebelumnya - tetapi sekarang, pendengar akan menangani peristiwa tersebut secara serentak dalam rangkaian yang terpisah .

4. Acara Kerangka Sedia Ada

Spring sendiri menerbitkan pelbagai acara di luar kotak. Sebagai contoh, ApplicationContext akan mengaktifkan pelbagai acara kerangka. Contohnya ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent dll.

Acara ini memberi pilihan kepada pembangun aplikasi untuk mengikuti kitaran hidup aplikasi dan konteksnya dan menambahkan logik tersuai mereka sendiri jika diperlukan.

Berikut adalah contoh ringkas pendengar yang mendengar penyegaran konteks:

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

Untuk mengetahui lebih lanjut mengenai acara rangka kerja yang ada, lihat tutorial seterusnya kami di sini.

5. Pendengar Acara yang Didorong oleh Anotasi

Bermula dengan Spring 4.2, pendengar acara tidak perlu menjadi kacang yang melaksanakan antara muka ApplicationListener - ia boleh didaftarkan pada kaedah umum kacang yang diuruskan melalui anotasi @EventListener :

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

Seperti sebelumnya, tandatangan kaedah menyatakan jenis peristiwa yang digunakannya.

Secara lalai, pendengar dipanggil secara serentak. Walau bagaimanapun, kami dapat menjadikannya tidak segerak dengan menambahkan anotasi @Async . Kita mesti ingat untuk mengaktifkan sokongan Async dalam aplikasi itu.

6. Sokongan Generik

Anda juga boleh menghantar acara dengan maklumat generik dalam jenis acara.

6.1. Acara Aplikasi Generik

Mari buat jenis acara generik . Dalam contoh kami, kelas acara mempunyai kandungan dan penunjuk status kejayaan :

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

Perhatikan perbezaan antara GenericSpringEvent dan CustomSpringEvent . Kami kini mempunyai fleksibiliti untuk menerbitkan sebarang peristiwa sewenang-wenangnya dan tidak perlu lagi diperluas dari ApplicationEvent .

6.2. Seorang Pendengar

Sekarang mari buat pendengar acara itu . Kami dapat menentukan pendengar dengan melaksanakan antara muka 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 ringkas ini, kami membahas asas-asas menangani peristiwa pada musim bunga - membuat acara khas sederhana, menerbitkannya, dan kemudian mengatasinya dalam pendengar.

Kami juga melihat ringkas bagaimana mengaktifkan pemprosesan peristiwa tidak segerak dalam konfigurasi.

Kemudian kami belajar tentang penambahbaikan yang diperkenalkan pada musim bunga 4.2, seperti pendengar yang didorong oleh anotasi, sokongan generik yang lebih baik, dan peristiwa yang mengikat fasa transaksi.

Seperti biasa, kod yang disajikan dalam artikel ini terdapat di Github. Ini adalah projek berasaskan Maven, jadi mudah diimport dan dijalankan sebagaimana adanya.