1概要

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

イベントは、フレームワーク内で見過ごされがちな機能の1つですが、より有用なものの1つです。そして、Springの他の多くのことと同様に、イベント発行は

ApplicationContext.

によって提供される機能の1つです。

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

  • イベントは

    ApplicationEvent

    を拡張する必要があります

  • 発行者は

    ApplicationEventPublisher

    オブジェクトを注入する必要があります。

  • リスナーは

    ApplicationListener

    インターフェースを実装するべきです


2カスタムイベント

Springでは、カスタムイベントを作成して公開することができます。これは、デフォルトでは

同期です

。これにはいくつかの利点があります。たとえば、リスナーが発行者のトランザクションコンテキストに参加できるようになるなどです。


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

  • 単純なイベントクラス** を作成しましょう – イベントデータを格納するための単なるプレースホルダーです。この場合、イベントクラスは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. 出版社

それでは、

そのイベントの発行者を作成しましょう

。発行者はイベントオブジェクトを作成し、それを聞いている人には誰でも発行できます。

イベントを発行するために、発行者は単に

ApplicationEventPublisher

を注入し、

publishEvent()

APIを使用するだけです。

@Component
public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void doStuffAndPublishAnEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
        applicationEventPublisher.publishEvent(customSpringEvent);
    }
}

代わりに、publisherクラスは

ApplicationEventPublisherAware

インターフェースを実装することができます – これはアプリケーションの起動時にイベントパブリッシャーを注入するでしょう。通常、発行者に[email protected]を挿入するほうが簡単です。


2.3. リスナー

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

リスナーの唯一の要件は、Beanであり、

ApplicationListener

インターフェースを実装することです。

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

私たちのカスタムリスナーがジェネリック型のカスタムイベントでどのようにパラメータ化されているかに注目してください。

これにより、オブジェクトが特定のイベントクラスのインスタンスであるかどうかを確認してキャストする必要もなくなります。

そして、すでに説明したように – デフォルトで

スプリングイベントは同期



doStuffAndPublishAnEvent()

メソッドは、すべてのリスナーがイベントの処理を終了するまでブロックします。


3非同期イベントの作成

場合によっては、イベントを同期的に公開することが、実際には探しているものではありません –

イベントの非同期処理

が必要な場合があります。

エグゼキュータを使って

ApplicationEventMulticaster

Beanを作成することで、設定でこれを有効にできます。ここでの目的のために、

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<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent cse) {
        System.out.println("Handling context re-freshed event. ");
    }
}

既存のフレームワークイベントの詳細については、https://www.baeldung.com/spring-context-events[次のチュートリアルはこちら]をご覧ください。


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

Spring 4.2以降、イベントリスナは

ApplicationListener

インタフェースを実装するBeanである必要はなく、

@ EventListener

アノテーションを介してマネージドBeanの任意の

public

メソッドに登録できます。

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

前述のように、メソッドシグネチャは、それが消費するイベントタイプを宣言します。以前と同様に、このリスナーは同期的に呼び出されます。しかし、非同期にすることは

@ Async

アノテーションを追加するのと同じくらい簡単です(アプリケーションにhttps://www.baeldung.com/spring-async#enable-async-support[enable

Async

support]を忘れないでください)。

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

イベントタイプの総称情報を使用してイベントを送出することもできます。


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

  • 一般的なイベントタイプを作成しましょう** 。この例では、イベントクラスは任意のコンテンツと

    success

    ステータスインジケータを保持します。

public class GenericSpringEvent<T> {
    private T what;
    protected boolean success;

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


GenericSpringEvent



CustomSpringEvent

の違いに注目してください。これで任意のイベントを公開できるようになり、

ApplicationEvent

から拡張する必要がなくなりました。


6.2. リスナー

それでは

そのイベントのリスナー

を作成しましょう。以前のようにApplicationListener__インターフェースを実装することでリスナーを定義できます。

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

しかし残念ながら、この定義では、

ApplicationEvent

クラスから

GenericSpringEvent

を継承する必要があります。それでは、このチュートリアルでは、

前述

で説明したアノテーション駆動型のイベントリスナーを利用しましょう。


@ EventListener

アノテーションにブール値のSpEL式を定義することで

イベントリスナを条件付き

にすることも可能です。この場合、イベントハンドラーは

String



GenericSpringEvent

が成功した場合にのみ呼び出されます。

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


Spring Expression Language(SpEL)

は、別のチュートリアルで詳しく説明されている強力な式言語です。


6.3. 出版社

イベント発行者は、リンク#publisher[上記]のリンクと似ています。しかし、型が消去されるため、フィルタリングするジェネリックスパラメータを解決するイベントを発行する必要があります。たとえば、

class GenericStringSpringEventはGenericSpringEvent <String>

を拡張します。

そして、

イベントを公開するための別の方法

があります。結果として

@ EventListener

というアノテーションが付けられたメソッドからnull以外の値を返すと、Spring Frameworkはその結果を新しいイベントとして私たちに送信します。

さらに、イベント処理の結果としてそれらをコレクションに戻すことで、複数の新しいイベントを発行することができます。


7. トランザクションバウンドイベント

この段落は

@ TransactionalEventListener

アノテーションの使用についてです。トランザクション管理の詳細については、https://www.baeldung.com/transaction-configuration-with-jpa-and-spring[Spring and JPAとのトランザクション]チュートリアルをご覧ください。

Spring 4.2以降、フレームワークは新しい

@ TransactionalEventListener

アノテーションを提供します。これは

@ EventListener

の拡張で、イベントのリスナーをトランザクションのフェーズにバインドすることを可能にします。以下のトランザクションフェーズにバインドすることができます。

トランザクションが発生した場合、イベントを起動するために**

AFTER

COMMIT__(デフォルト)が使用されます

  • 正常に完了しました**


  • AFTER

    ROLLBACK__ – トランザクションが

    ロールバックした場合


  • AFTER

    COMPLETION__ – トランザクションが

    完了

    した場合(** のエイリアス


AFTER

COMMIT

および

AFTER

ROLLBACK




BEFORE

COMMIT__は、トランザクションの直前に

イベントを起動するために使用されます。

  • コミット**

トランザクションイベントリスナの簡単な例を示します。

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

このリスナーは、イベントプロデューサが実行されていてコミットしようとしているトランザクションがある場合にのみ呼び出されます。

また、トランザクションが実行されていない場合、

fallbackExecution

属性を

true

に設定してこれをオーバーライドしない限り、イベントはまったく送信されません。


8結論

この簡単なチュートリアルでは、

Springでのイベント処理

– 簡単なカスタムイベントの作成、公開、そしてリスナでの処理の基本について説明しました。

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

次に、Spring 4.2で導入された、アノテーション主導のリスナー、より優れたジェネリックスのサポート、トランザクションフェーズに結びつくイベントなど、改良点について学びました。

いつものように、この記事で紹介されているコードはhttps://github.com/eugenp/tutorials/tree/master/spring-all[Githubで利用可能]です。これはMavenベースのプロジェクトなので、そのままインポートして実行するのは簡単なはずです。