春のイベント

1。概要

この記事では、Springでイベントを使用する方法について説明します

イベントは、フレームワークで見過ごされがちな機能の1つですが、より便利な機能の1つでもあります。そして、Springの他の多くのものと同様に、イベントの公開はApplicationContextによって提供される機能の1つです

従うべきいくつかの簡単なガイドラインがあります:

  • イベントはApplicationEventを拡張する必要があります
  • パブリッシャーはApplicationEventPublisherオブジェクトを挿入する必要があります
  • リスナーはApplicationListenerインターフェースを実装する必要があります

2.カスタムイベント

Springを使用すると、デフォルトで同期するカスタムイベントを作成して公開できます。これにはいくつかの利点があります。たとえば、リスナーがパブリッシャーのトランザクションコンテキストに参加できるなどです。

2.1。簡単なアプリケーションイベント

単純なイベントクラスを作成ましょう–イベントデータを格納するための単なるプレースホルダーです。この場合、イベントクラスは文字列メッセージを保持します。

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。出版社

それでは、そのイベントの発行者を作成ましょう。パブリッシャーはイベントオブジェクトを作成し、それを聞いている人に公開します。

イベントを公開するには、パブリッシャーはApplicationEventPublisherを挿入し、publishEvent() APIを使用するだけです。

@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); } }

または、パブリッシャークラスでApplicationEventPublisherAwareインターフェイスを実装することもできます。これにより、アプリケーションの起動時にイベントパブリッシャーが挿入されます。通常、パブリッシャーに@Autowireを挿入する方が簡単です。

2.3。リスナー

最後に、リスナーを作成しましょう。

リスナーの唯一の要件は、Beanであり、ApplicationListenerインターフェースを実装することです。

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

カスタムリスナーが汎用タイプのカスタムイベントでパラメーター化されていることに注目してください。これにより、onApplicationEvent()メソッドがタイプセーフになります。これにより、オブジェクトが特定のイベントクラスのインスタンスであるかどうかを確認し、キャストする必要もなくなります。

また、すでに説明したように(デフォルトでSpringイベントは同期的ですdoStuffAndPublishAnEvent()メソッドは、すべてのリスナーがイベントの処理を完了するまでブロックします。

3.非同期イベントの作成

場合によっては、イベントを同期的に公開することが実際には私たちが探しているものではありません。イベントの非同期処理が必要になる場合があります

エグゼキュータを使用してApplicationEventMulticasterBeanを作成することにより、構成でこれをオンにできます。ここでの目的のために、SimpleAsyncTaskExecutorはうまく機能します:

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

イベント、パブリッシャー、およびリスナーの実装は以前と同じままですが、リスナーは別のスレッドでイベントを非同期的に処理します

4.既存のフレームワークイベント

Spring自体は、箱から出してさまざまなイベントを公開しています。たとえば、ApplicationContextはさまざまなフレームワークイベントを発生させます。例:ContextRefreshedEvent、ContextStartedEvent、RequestHandledEventなど。

これらのイベントは、アプリケーション開発者に、アプリケーションのライフサイクルとコンテキストにフックし、必要に応じて独自のカスタムロジックを追加するオプションを提供します。

コンテキストの更新をリッスンするリスナーの簡単な例を次に示します。

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

既存のフレームワークイベントの詳細については、こちらの次のチュートリアルをご覧ください。

5.アノテーション駆動型イベントリスナー

Spring 4.2以降、イベントリスナーはApplicationListenerインターフェースを実装するBeanである必要はありません。@ EventListenerアノテーションを介してマネージドBeanの任意のパブリックメソッドに登録できます。

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

以前と同様に、メソッドシグネチャはそれが消費するイベントタイプを宣言します。

デフォルトでは、リスナーは同期的に呼び出されます。ただし、@ Asyncアノテーションを追加することで、簡単に非同期にすることができます。ただし、アプリケーションで非同期サポートを有効にすることを忘れないでください。

6.ジェネリックサポート

イベントタイプのジェネリック情報を使用してイベントをディスパッチすることもできます。

6.1。一般的なアプリケーションイベント

Let's create a generic event type. In our example, the event class holds any content and a success status indicator:

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

Notice the difference between GenericSpringEvent and CustomSpringEvent. We now have the flexibility to publish any arbitrary event and it's not required to extend from ApplicationEvent anymore.

6.2. A Listener

Now let’s create a listener of that event. We could define the listener by implementing the ApplicationListener interface like before:

@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

このクイックチュートリアルでは、Springでのイベント処理の基本、つまり、単純なカスタムイベントを作成して公開し、リスナーで処理する方法について説明しました。

また、構成内のイベントの非同期処理を有効にする方法についても簡単に説明しました。

次に、アノテーション駆動型リスナー、より優れたジェネリックサポート、トランザクションフェーズへのイベントバインディングなど、Spring4.2で導入された改善点について学びました。

いつものように、この記事で紹介するコードはGithubで入手できます。これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。