CDI 2.0のイベント通知モデルの紹介

1概要

CDI (Contexts and Dependency Injection)はJakarta EEプラットフォームの標準の依存性注入フレームワークです。

このチュートリアルでは、http://www.cdi-spec.org/news/2017/05/15/CDI 2 is__released/[CDI 2.0]と、それが強力でタイプセーフなインジェクションメカニズムの上にどのように構築されるかを見ていきますCDI 1.xの機能強化、フル機能のイベント通知モデルの追加**

2 Mavenの依存関係

はじめに、簡単なMavenプロジェクトを作成します。

  • CDI 2.0準拠のコンテナが必要です。CDIの参照実装であるhttp://weld.cdi-spec.org/[Weld]が適しています。

<dependencies>
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>2.0.SP1</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.weld.se</groupId>
        <artifactId>weld-se-core</artifactId>
        <version>3.0.5.Final</version>
    </dependency>
</dependencies>

いつものように、私達はhttps://search.maven.org/search?q=g:javax.enterprise%20AND%20a:cdi-apiの最新バージョンを引き出すことができます。

3カスタムイベントの監視と処理

簡単に言えば、 CDI 2.0イベント通知モデルは、https://docs.jboss.org/に基づいて、https://www.baeldung.com/java-observer-pattern[Observer pattern] の古典的な実装です。 cdi/api/1.0/javax/enterprise/event/Observes.html[ @ Observes ]メソッドパラメータアノテーション。したがって、オブザーバメソッドを簡単に定義できます。オブザーバメソッドは、1つ以上のイベントに応じて自動的に呼び出すことができます。

たとえば、1つ以上のBeanを定義して、1つ以上の特定のイベントを発生させる一方で、他のBeanにイベントについて通知し、それに応じて対応することができます。

これがどのように機能するかをより明確に示すために、基本サービスクラス、カスタムイベントクラス、およびカスタムイベントに反応するオブザーバメソッドを含む簡単な例を作成します。

** 3.1. 基本サービスクラス

**

単純な TextService クラスを作成することから始めましょう。

public class TextService {

    public String parseText(String text) {
        return text.toUpperCase();
    }
}

3.2. カスタムイベントクラス

次に、コンストラクタで String 引数を取るサンプルイベントクラスを定義しましょう。

public class ExampleEvent {

    private final String eventMessage;

    public ExampleEvent(String eventMessage) {
        this.eventMessage = eventMessage;
    }

   //getter
}

3.3. @ Observes アノテーション を使用したオブザーバメソッドの定義

サービスクラスとイベントクラスを定義したので、 ExampleEvent クラスのオブザーバメソッドを作成するために @ Observes アノテーションを使用しましょう。

public class ExampleEventObserver {

    public String onEvent(@Observes ExampleEvent event, TextService textService) {
        return textService.parseText(event.getEventMessage());
    }
}

一見したところでは、 onEvent() メソッドの実装はかなり簡単に見えますが、実際には @ Observes アノテーションを通して多くの機能をカプセル化しています。

ご覧のとおり、 onEvent() メソッドは ExampleEvent および TextService オブジェクトを引数としてとるイベントハンドラです。

  • @ Observes アノテーションの後に指定された引数はすべて標準のインジェクションポイントであることに注意してください。

3.4. CDI 2.0コンテナを初期化する

この時点で、サービスクラスとイベントクラスを作成し、イベントに対応するための単純なオブザーバメソッドを定義しました。しかし、実行時にこれらのインスタンスをインジェクトするようにCDIにどのように指示するのでしょうか。

ここで、イベント通知モデルはその機能を最大限に発揮します。新しいhttps://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/se/SeContainer.html[ SeContainer ]実装を単純に初期化し、 fireEvent() メソッドを通じて1つ以上のイベントを発生させます。

SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance();
try (SeContainer container = containerInitializer.initialize()) {
    container.getBeanManager().fireEvent(new ExampleEvent("Welcome to Baeldung!"));
}

Jakarta EEではなくJava SE環境でCDIを使用しているため、 SeContainerInitializer および SeContainer オブジェクトを使用していることに注意してください。

ExampleEvent がイベント自体を伝播することによって起動されると、添付されているすべてのオブザーバメソッドに通知されます。

@ Observes アノテーションの後に引数として渡されたすべてのオブジェクトは完全に初期化されるので、CDIは TextService オブジェクトグラフ全体を onEvent() メソッドに挿入する前に配線します。

一言で言えば、 型安全なIoCコンテナと、機能豊富なイベント通知モデル の利点があります。

4 ContainerInitialized イベント

前の例では、カスタムイベントを使用してイベントをオブザーバメソッドに渡し、完全に初期化された TextService オブジェクトを取得しました。

もちろん、これはアプリケーションの複数のポイントにまたがって1つ以上のイベントを伝播する必要がある場合に役立ちます。

  • 場合によっては、追加のイベントを実装しなくても、アプリケーションクラス内で使用できるように完全に初期化されたオブジェクトの束を取得するだけで済みます。

このために、** CDI 2.0はhttp://javadox.com/org.jboss.weld.se/weld-se-core/2.2.6.Final/org/jboss/weld/environment/se/events/ContainerInitializedを提供しています。 .html[ ContainerInitialized ]イベントクラス。Weldコンテナが初期化されたときに自動的に発生します。

ExampleEventObserver クラスに制御を転送するために ContainerInitialized イベントを使用する方法を見てみましょう。

public class ExampleEventObserver {
    public String onEvent(@Observes ContainerInitialized event, TextService textService) {
        return textService.parseText(event.getEventMessage());
    }
}

また、 ContainerInitialized イベントクラスはWeld固有のものであることにも注意してください。したがって、別のCDI実装を使用する場合は、オブザーバメソッドをリファクタリングする必要があります。

5条件付きオブザーバ法

現在の実装では、 ExampleEventObserver クラスはデフォルトで無条件オブザーバメソッドを定義します。これは、クラスのインスタンスが現在のコンテキストに存在するかどうかにかかわらず、** オブザーバメソッドは常に指定されたイベントを通知されることを意味します。

同様に、 @ Observes アノテーションの引数として notifyObserver = IF EXISTS__を指定することで、条件付きオブザーバメソッドを** 定義することができます。

public String onEvent(@Observes(notifyObserver=IF__EXISTS) ExampleEvent event, TextService textService) {
    return textService.parseText(event.getEventMessage());
}

条件付きオブザーバメソッドを使用すると、オブザーバメソッドを定義するクラスのインスタンスが現在のコンテキスト内に存在する場合にのみ、そのメソッドに一致イベントが通知されます。

** 6. トランザクションオブザーバメソッド

  • データベースの更新や削除操作など、トランザクション内でイベントを発生させることもできます** 。そうするために、 during 引数を @ Observes アノテーションに追加することでトランザクションオブザーバメソッドを定義できます。

during 引数に指定できる値はそれぞれ、トランザクションの特定の段階に対応します。

  • BEFORE COMPLETION__

  • 完了後

  • AFTER SUCCESS__

  • AFTER FAILURE__

トランザクション内で ExampleEvent イベントを起動した場合は、必要なフェーズでイベントを処理するために、 onEvent() メソッドを適切にリファクタリングする必要があります。

public String onEvent(@Observes(during=AFTER__COMPLETION) ExampleEvent event, TextService textService) {
    return textService.parseText(event.getEventMessage());
}

トランザクションオブザーバメソッドは、与えられたトランザクションのマッチングフェーズでのみ、提供されたイベントを通知されます

** 7. オブザーバメソッド

CDI 2.0のイベント通知モデルに含まれるもう1つの優れた改善点は、特定のイベントのオブザーバーを呼び出すための順序付けや優先順位を設定できることです。

@ Observesの後にhttps://docs.oracle.com/javaee/7/api/javax/annotation/Priority.html[ @Priority ]注釈を指定することで、オブザーバメソッドが呼び出される順序を簡単に定義できます。 .

この機能の仕組みを理解するために、 ExampleEventObserver が実装するものとは別に、別のオブザーバメソッドを定義しましょう。

public class AnotherExampleEventObserver {

    public String onEvent(@Observes ExampleEvent event) {
        return event.getEventMessage();
    }
}
  • この場合、デフォルトでは両方のオブザーバメソッドが同じ優先度を持ちます。したがって、CDIがそれらを呼び出す順序は、単に予測不可能です** 。

@ Priority アノテーションを通して各メソッドに呼び出し優先順位を割り当てることでこれを簡単に修正できます。

public String onEvent(@Observes @Priority(1) ExampleEvent event, TextService textService) {
   //... implementation
}
public String onEvent(@Observes @Priority(2) ExampleEvent event) {
   //... implementation
}
  • 優先順位は自然な順序に従います。** したがって、CDIは優先順位 1 のオブザーバメソッドを最初に呼び出し、次に優先順位 2 のメソッドを呼び出します。

同様に、 2つ以上のメソッドにわたって同じ優先順位レベルを使用する場合、順序は再び定義されません

8非同期イベント

これまでに学んだすべての例では、イベントを同期的に実行しました。ただし、CDI 2.0では非同期イベントも簡単に起動できます。非同期オブザーバメソッドは、これらの非同期イベントを異なるスレッドで処理することができます。

fireAsync() メソッドを使って非同期にイベントを発生させることができます。

public class ExampleEventSource {

    @Inject
    Event<ExampleEvent> exampleEvent;

    public void fireEvent() {
        exampleEvent.fireAsync(new ExampleEvent("Welcome to Baeldung!"));
    }
}
  • Beansはイベントを起動します。これは Event インターフェースの実装です。したがって、他の従来のBean ** と同様に注入することができます。

非同期イベントを処理するには、https://docs.jboss.org/cdi/api/2.0.EDR2/javax/enterprise/event/ObservesAsync.html[ @ObservesAsync ]を使用して1つ以上の非同期オブザーバメソッドを定義する必要があります。アノテーション:

public class AsynchronousExampleEventObserver {

    public void onEvent(@ObservesAsync ExampleEvent event) {
       //... implementation
    }
}

9結論

この記事では、CDI 2.0にバンドルされている改善されたイベント通知モデルを使い始める方法を学びました。

いつもどおり、このチュートリアルに示されているすべてのコードサンプルはhttps://github.com/eugenp/tutorials/tree/master/cdi/src/main/java/com/baeldung/cdi2observers[GitHub]から入手できます。