1. 概要

このチュートリアルでは、Springでのイベントの使用方法について説明します。

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

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

  • Spring Framework 4.2より前のバージョンを使用している場合、イベントクラスはApplicationEventを拡張する必要があります。 4.2バージョン以降、イベントクラスは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を注入する方が簡単です。

Spring Framework 4.2以降、 ApplicationEventPublisher インターフェースは、任意のオブジェクトをイベントとして受け入れる publishEvent(Object event)メソッドに新しいオーバーロードを提供します。 したがって、SpringイベントはApplicationEventクラスを拡張する必要がなくなりました。

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

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

また、すでに説明したように(デフォルトでは、 Springイベントは同期)、 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. ");
    }
}

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

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

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

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

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

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

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
}

GenericSpringEventCustomSpringEventの違いに注意してください。 これで、任意のイベントを公開する柔軟性が得られ、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式を定義することにより、イベントリスナーを条件付きにすることもできます。

この場合、イベントハンドラーは、StringGenericSpringEventが成功した場合にのみ呼び出されます。

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

Spring式言語(SpEL)は、別のチュートリアルで詳細に説明されている強力な式言語です。

6.3. 出版社

イベント発行者は、上記で説明したものと同様です。 ただし、型消去のため、フィルタリングするジェネリックパラメータを解決するイベントを公開する必要があります。たとえば、 クラスGenericStringSpringEventはGenericSpringEventを拡張します

また、イベントを公開する別の方法があります。結果として @EventListener アノテーションが付けられたメソッドからnull以外の値を返す場合、SpringFrameworkはその結果を次のように送信します。私たちのための新しいイベント。 さらに、イベント処理の結果としてコレクションに返すことで、複数の新しいイベントを公開できます。

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

このセクションでは、@TransactionalEventListenerアノテーションの使用について説明します。 トランザクション管理の詳細については、 TransactionsWithSpringおよびJPAを確認してください。

Spring 4.2以降、フレームワークは新しい @TransactionalEventListener アノテーションを提供します。これは、 @EventListener の拡張であり、イベントのリスナーをトランザクションのフェーズにバインドできます。

次のトランザクションフェーズへのバインドが可能です。

  • AFTER_COMMIT (デフォルト)は、トランザクションが正常に完了した場合にイベントを発生させるために使用されます。
  • AFTER_ROLLBACK –トランザクションがロールバックされた場合
  • AFTER_COMPLETION –トランザクションが完了した場合AFTER_COMMITおよびAFTER_ROLLBACKのエイリアス)
  • BEFORE_COMMIT は、トランザクションcommitの前にイベントrightを発生させるために使用されます。

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

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

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

また、実行中のトランザクションがない場合、fallbackExecution属性をtrueに設定してこれをオーバーライドしない限り、イベントはまったく送信されません。

8. 結論

この簡単な記事では、 Spring でのイベントの処理の基本について説明しました。これには、単純なカスタムイベントの作成、公開、リスナーでの処理が含まれます。

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

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

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