春のイベント

春のイベント

1. 概要

この記事では、how to use events in Springについて説明します。

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

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

  • イベントはApplicationEventを拡張する必要があります

  • 出版社はApplicationEventPublisherオブジェクトを挿入する必要があります

  • リスナーはApplicationListenerインターフェースを実装する必要があります

2. カスタムイベント

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

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

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

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

イベントを公開するには、パブリッシャーは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);
    }
}

または、パブリッシャークラスで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 events are synchronous)、doStuffAndPublishAnEvent()メソッドは、すべてのリスナーがイベントの処理を終了するまでブロックします。

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

場合によっては、イベントを同期的に公開することは、実際には私たちが探しているものではありません–we may need async handling of our events

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

@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster =
          new SimpleApplicationEventMulticaster();

        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return eventMulticaster;
    }
}

イベント、パブリッシャー、リスナーの実装は以前と同じですが、現在はthe listener will asynchronously deal with the event in a separate threadです。

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

Spring自体は、さまざまなイベントをすぐに公開しています。 たとえば、ApplicationContextはさまざまなフレームワークイベントを発生させます。 E.g. ContextRefreshedEvent, ContextStartedEvent, RequestHandledEventなど

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

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

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

既存のフレームワークイベントの詳細については、our next tutorial hereを参照してください。

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アノテーションを追加するのと同じくらい簡単です(アプリケーションでenable Async supportを忘れないでください)。

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

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

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

Let’s create a generic event type。 この例では、イベントクラスは任意のコンテンツとsuccessステータスインジケーターを保持します。

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

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

GenericSpringEventCustomSpringEventの違いに注意してください。 これで、任意のイベントを公開できる柔軟性が得られ、ApplicationEventから拡張する必要がなくなりました。

6.2. リスナー

それでは、a listener of that eventを作成しましょう。 以前のようにthe ApplicationListenerインターフェースを実装することで、リスナーを定義できます。

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

ただし、残念ながら、この定義では、ApplicationEventクラスからGenericSpringEventを継承する必要があります。 したがって、このチュートリアルでは、previouslyについて説明したアノテーション駆動型のイベントリスナーを利用しましょう。

@EventListenerアノテーションでブールSpEL式を定義することにより、make the event listener conditionalを作成することもできます。 この場合、イベントハンドラは、Stringの成功したGenericSpringEventに対してのみ呼び出されます。

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

Spring Expression Language (SpEL)は強力な表現言語であり、別のチュートリアルで詳しく説明されています。

6.3. 出版社

イベント発行者は、aboveで説明されているものと似ています。 ただし、型の消去により、フィルター処理するジェネリックパラメーターを解決するイベントを発行する必要があります。 たとえば、class GenericStringSpringEvent extends GenericSpringEvent<String>です。

そして、an alternative way of publishing eventsがあります。 結果として@EventListenerアノテーションが付けられたメソッドからnull以外の値を返す場合、SpringFrameworkはその結果を新しいイベントとして送信します。 さらに、イベント処理の結果としてそれらをコレクションとして返すことにより、複数の新しいイベントを発行できます。

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

この段落では、@TransactionalEventListenerアノテーションの使用について説明します。 トランザクション管理の詳細については、Transactions with Spring and JPAチュートリアルを確認してください。

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

  • AFTER_COMMIT(デフォルト)は、トランザクションにcompleted successfullyがある場合にイベントを発生させるために使用されます

  • AFTER_ROLLBACK –トランザクションにrolled backがある場合

  • AFTER_COMPLETION –トランザクションにcompletedAFTER_COMMITおよびAFTER_ROLLBACKのエイリアス)がある場合

  • BEFORE_COMMITは、イベント権を起動するために使用されます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. 結論

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

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

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

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