1. 概要

CDI (コンテキストと依存性注入)は、JakartaEEプラットフォームの標準的な依存性注入フレームワークです。

このチュートリアルでは、 CDI 2.0 と、が改善されたフル機能のイベント通知を追加することにより、CDI1.xの強力でタイプセーフなインジェクションメカニズムをどのように構築するかを見ていきます。モデル。

2. Mavenの依存関係

開始するには、単純なMavenプロジェクトをビルドします。

CDI 2.0準拠のコンテナが必要であり、CDIのリファレンス実装である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>

いつものように、MavenCentralからcdi-apiweld-se-coreの最新バージョンをプルできます。

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

簡単に言えば、 CDI 2.0イベント通知モデルは、 @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アノテーションを使用したオブザーバーメソッドの定義

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

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

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

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

@Observesアノテーションの後に指定されたすべての引数は、標準のインジェクションポイントであることに注意してください。 その結果、CDIは完全に初期化されたインスタンスを作成し、それらをオブザーバーメソッドに挿入します。

3.4. CDI2.0コンテナの初期化

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

ここで、イベント通知モデルがその機能を最大限に発揮します。 新しいSeContainer実装を初期化し、 fireEvent()メソッドを介して1つ以上のイベントを発生させるだけです。

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

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

ExampleEvent が発生すると、イベント自体を伝播することにより、接続されているすべてのオブザーバーメソッドに通知されます。

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

一言で言えば、機能豊富なイベント通知モデルに加えて、タイプセーフなIoCコンテナの利点があります。

4. ContainerInitializedイベント

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

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

場合によっては、追加のイベントの実装を行わなくても、アプリケーションクラス内ですぐに使用できる完全に初期化されたオブジェクトの束を取得する必要があります。

この目的のために、 CDI 2.0はContainerInitializedイベントクラスを提供します。このイベントクラスは、Weldコンテナが初期化されると自動的に発生します

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

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の後に@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[のメソッドを呼び出します。 X203X]。

同様に、 2つ以上のメソッドで同じ優先度レベルを使用する場合、順序は再び未定義になります

8. 非同期イベント

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

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

public class ExampleEventSource {
    
    @Inject
    Event<ExampleEvent> exampleEvent;
    
    public void fireEvent() {
        exampleEvent.fireAsync(new ExampleEvent("Welcome to Baeldung!"));
    }   
}

Beanは、イベントインターフェイスの実装であるイベントを起動します。 したがって、他の従来のBeanと同じように注入できます。

非同期イベントを処理するには、@ObservesAsyncアノテーションを使用して1つ以上の非同期オブザーバーメソッドを定義する必要があります。

public class AsynchronousExampleEventObserver {

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

9. 結論

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

いつものように、このチュートリアルに示されているすべてのコードサンプルは、GitHubから入手できます。