Contexts and Dependency Injection(CDI)イベントの作成と使用

概要

    目的

    このチュートリアルでは、Contexts and Dependency Injection(CDI)によるイベントの作成と使用について説明します。

    所要時間

    約45分

    はじめに

    イベントは、JavaBeansモデルの重要な部分です。 コンポーネントにリスナーを登録することで、Abstract Windowing Toolkit(AWT)やSwingアプリケーションでイベントを使用したことがあるのではないでしょうか。 コンポーネント(ボタンなど)をクリックすると、イベントが登録リスナーによって起動および処理されます。

    Contexts and Dependency Injectionは、Java Platform, Enterprise Edition(Java EE)でJSR-299として導入されました。 CDIのContextsの部分により、Beanは別のBeanのライフ・サイクルを共有して、これに参加することができます。また、CDIのDependency Injectionの部分により、Beanは別のBeanのインスタンスへの参照を注入するだけで、その参照を取得できます。 CDI機能は、インスタンス化プロセスを排除すると同時に強い型チェックを実行することで、クラスを疎結合する機能です。 CDIは、別のBeanへの参照を作成するための@Injectアノテーションを提供します(古いコンテキストの参照プロセスを回避)。 CDIは、コンパイラがエラーを検出できるように、文字列ベースの参照を排除して入力を実行します。 CDIは、統合開発環境(IDE)と非常にスムーズに連動します。

    CDIイベント・モデルにより、Beanはコンパイル時に依存関係を設定せずに、他のBeanと相互にイベントを送信することができ、自身のイベント・ペイロード(イベントに関する情報を含んだクラス)を作成できます。 CDIイベントを使用することで、あるBeanはイベントを定義し、別のBeanはイベントを起動し、もう1つのBeanはイベントを処理することが可能になります。 また、他のイベント・モデルとは違って、リスナー(オブザーバ)をリスナーとして登録せずに追加できます。

    シナリオ

    このチュートリアルでは、「Java EE 6: Build Database Applications with JPA」というコースのオークション・アプリケーションの一部を使用します。 このアプリケーションでは、ウォッチャー(オークションの品目で変更が生じた場合に通知を希望する人)を登録する最初のビュー・ページを使用します。 このチュートリアルでは、ウォッチャーを簡単な文字列でListオブジェクトに追加します(本番の電子メール・アドレスまたはインスタント・メッセージングIDなど)。 ウォッチャーは、コースのオークション・アプリケーションの場合と同様に、アプリケーション全体の一部として追加することもできます。


    項目の入札期間中、オークションの項目に対して応札が行われると、新しい応札、応札が行われた時間がウォッチャーに通知されます。


    このシナリオでは、Observer設計パターンについて説明します。この設計パターンでは、ウォッチャーは、イベントの通知に任意に自己登録するオブザーバとなります。

    ソフトウェア要件

    ハードウェアとソフトウェアの要件のリストは、以下のとおりです。

    • Java Platform, Standard Edition(Java SE)7(Java SE 7u11を推奨)
    • NetBeans IDE 7.x Java EEバージョン(NetBeans 7.2を推奨)
    • GlassFish 3.1.2またはOracle WebLogic Server 12c

    前提条件

    このチュートリアルを始める前に以下のことを確認してください。

    • Java EEアプリケーションの作成とデプロイの経験がある。
    • NetBeans 7.2 Java EEエディションをインストールし、起動してある。
    • AuctionWatcher.zipファイルを解凍してある。
    • NetBeansでAuctionWatcherプロジェクトを開いてある。
    • (オプション)Oracle WebLogic Server 12cをインストール済みである。 WebLogic ServerのインストールおよびNetBeansへの追加について詳しくは、リソース・セクションを参照してください。

イベント・クラスの作成

    イベントは単なる文字列にすることができ、その場合はイベント・クラスを作成する必要がありません。 ただし、文字列以上のものをリスナーに渡す場合は、イベント情報(ペイロード)を格納するための簡単なPlain Old Java Object(POJO)を作成します。

    Source Packages」を右クリックして、「New」→「Java Package」を選択します。


    イベント・クラスを格納するためのパッケージがAuctionWatcherアプリケーションに追加されます。

    パッケージ名としてeventと入力し、「Finish」をクリックします。



    新しいパッケージを右クリックして、「New」→「Java Class」を選択します。


    クラス名としてAuctionEventと入力し、「Finish」をクリックします。


    オークションのStringの記述、現在の項目の価格(浮動)、Date timeStamp、Stringウォッチャーのリストを保持するためのフィールドをAuctionEventクラスに追加します。

        private String auctionDescription;
        private float price;
        private Date timeStamp;
        private List<String> notifyList;

    次のインポート文をクラス上部に追加します。

    import java.util.Date;
    import java.util.List;

    引数のないコンストラクタをクラスに追加します。 フィールドの下を右クリックして、「Insert Code」を選択します(または[Alt]を押しながら[Insert]を押します)。 次に、Generateダイアログ・ボックスから「Constructor」を選択します。

    フィールドを選択せずに、引数のないコンストラクタを作成して、「Generate」をクリックします。

    注:フィールドを選択して、引数を取るコンストラクタを作成することもできます。

    同じコード生成方法を使用して、各フィールドのgetterとsetterを追加します。

    a [Alt]を押しながら[Insert]を押して、Generateダイアログ・ボックスから「Getter and Setter」を選択します。

    b 「AuctionEvent」クラス名を選択してすべてのフィールドを選択し、「Generate」をクリックします。


    ファイルを保存します。

イベント修飾子の作成

    CDI修飾子はCDIイベントでは必要ありませんが、このチュートリアルでは、修飾子を作成してオークション・イベント(Bid)の特定のタイプを識別します。 後で説明するように、修飾子を使用すると、どのメソッドがイベントを受け取るのかを絞り込むことができます。

    イベント・パッケージの下に、修飾子を保持するための別のパッケージを作成します。

    a イベント・パッケージを右クリックして、「New」→「Java Package」を選択します。

    b パッケージ名としてqualifierと入力し、「Finish」をクリックします。


    修飾子パッケージにCDI修飾子を作成します。

    a 修飾子パッケージを右クリックして、「New」→「Other」を選択します。

    b Categoriesから「Contexts and Dependency Injection」を選択し、File Typesから「Qualifier Type」を選択します。

    c 「Next」をクリックします。


    修飾子のクラス名としてBidと入力し、「Finish」をクリックします。


    CDI修飾子が自動生成されます。

    修飾子は、Beanタイプの定義を絞り込むために、Beanにアノテーションとして追加できるタイプを作成します。 Beanに修飾子がない場合は、CDIによって@Defaultが適用されます。 @Bid修飾子をメソッド、フィールド、パラメータ、または別のタイプに適用できます。

イベント・ハンドラの作成

    イベント・ハンドラ・クラスは、イベントを起動し、AuctionEventオブジェクトを渡すメソッドを含んだ任意のJavaクラスにすることができます。

    オブザーバを保持するための別のパッケージを作成します。

    a 「Source Packages」を右クリックして、「New」→「Java Package」を選択します。

    b パッケージ名としてobserverと入力し、「Finish」をクリックします。

    WatcherEventHandlerというPOJOをオブザーバ・パッケージに追加します。


    以下の手順に従います。

    a 引数のないコンストラクタをクラスに追加します。

    b AuctionEvent eventをパラメータとして取るnotifyWatchersというメソッドを追加します。

    c パラメータ・リストで、CDI @Observesアノテーションおよび作成した修飾子@Bidを追加します。

        public WatcherEventHandler() {
        }

        public void notifyWatchers (@Observes @Bid AuctionEvent event) {
            System.out.println("In notifyWatchers");
            for (String s : event.getNotifyList()) {
                System.out.println("Notifying watcher: " + s + " of bid on auction: "
                        + event.getAuctionDescription()
                        + " price changed to: $" + event.getPrice()
                        + " at: " + event.getTimeStamp());
            }
        }

    メソッド・パラメータに配置されたアノテーションは、@Bidの修飾子が付いたAuctionEventが起動したときに、このメソッドが起動することを意味します。 このメソッドをアプリケーションで直接呼び出すこともできますが、@Observesアノテーションを使用すると、イベント・ハンドラをコードから切り離すことができます。 notifyWatchersメソッドは、イベントのnotifyListフィールドの各ウォッチャーへのメッセージを書き込みます。 メッセージには、オークションの項目の現在の価格、応札が行われた日時が記載されています。

    [Ctrl]と[Shift]を押しながら[I]を押して、欠けているインポート文を修正し、ファイルを保存します。

JSF Beanへのコードの追加によるイベントの起動

    JSF BeanのAuctionBeanで、新しい入札がオークション項目に行われたときにイベントを起動するコードを追加します。

    タイプAuctionEventのjavax.enterprise.event.Eventオブジェクトの、@Bidの修飾子が付けられた注入済みインスタンスを追加します。

        @Inject @Bid
        private Event<AuctionEvent> bidEvent;

    注:これはCDIイベント注入なので、不適切な依存性に関するNetBeansの警告は無視してください。 警告をクリックすると、CDIイベント記号が表示されます。


    JSFパッケージでAuctionBeanを開き、placeBidメソッドを見つけます。


    注: コードの行番号は正確に一致しない場合があります。

    JSFMessage.addInfoMessageの行の下に次のコードを追加します。

                AuctionEvent eventPayload = new AuctionEvent();
                eventPayload.setNotifyList(watchers);
                eventPayload.setAuctionDescription(description);
                eventPayload.setPrice(currPrice);
                eventPayload.setTimeStamp(new Date());
                bidEvent.fire(eventPayload);

    このコードにより、現在のウォッチャー・リストからのAuctionEventペイロード、項目の説明、項目の現在の価格、現在の時間が作成されます。 次に、AuctionEventオブジェクトで、イベントがイベント・ペイロードとして起動されます。 @Bid AuctionEventイベント・タイプのオブザーバのみがこのイベントを受信します。

    ファイルを保存します。

AuctionWatcherアプリケーションの実行とテスト

    アプリケーションをサーバー(デフォルトではGlassFish Server 3.1.2)にデプロイします。


    ブラウザで、URL:localhost:8080/AuctionWatcherを入力して、アプリケーションを開きます。

    以下の手順に従います。

    a ウォッチャー(Tomなど)を追加して、「Add Watcher」をクリックします。

    b さらにウォッチャー(MattとCindy)を追加して、追加のたびに「Add Watcher」をクリックします。

    c 「Go To Item」をクリックします。

    ウォッチャーの名前の横にあるRemoveリンクをクリックすると、そのウォッチャーを削除できます。

    Itemビュー・ページで「Bid」ボタンをクリックして、この項目の入札を行います。

     

    GlassFish Serverのコンソール・ウィンドウで、起動したイベントがnotifyWatchersメソッドによって確認されます。このメソッドにより、ウォッチャーのリストが順次処理され、各ウォッチャーへの通知が表示されます。


    項目への新しい入札が行われるたびに、ウォッチャーに通知が送信されます。 また、このメソッドを使用して、各ウォッチャーにテキスト・メッセージまたは電子メールを送信できます。

CDIインターセプタからのイベントの起動

    Contexts and Dependency Injectionの中心テーマは疎結合です。 イベントを起動するためにJavaServer Faces(JSF)placeBidメソッドを変更することは、コードをイベント・メカニズムから切り離す最善の方法ではありません。Creating and using CDI Interceptors OBEに示すように、インターセプタからイベントを起動する方法がより望ましいアプローチです。

    AuctionBeanクラスのplaceBidメソッドのコードを削除するかコメント・アウトします。

    ヒント: コードの複数の行を素早くコメント・アウトするには、行を選択して、[Ctrl]と[Shift]を押しながら、[C]を押します。

    NotifyInterceptorクラスを開いて、EventとAuctionBeanの注入を追加します。

    イベントのBeanのプロパティにアクセスするには、AuctionBeanへの参照を注入する必要があります。

        @Inject @Bid
        private Event<AuctionEvent> bidEvent;
        @Inject
        private AuctionBean bean;

    入札が有効な場合(最低入札額より大きい値)にイベントを起動するように、notifyEventメソッドを変更します。 コードのcontext.proceed行の後にこのコードを追加します。

            if (bean.getBid() > bean.getMinBid()) {
                AuctionEvent eventPayload = new AuctionEvent();
                eventPayload.setNotifyList(bean.getWatchers());
                eventPayload.setAuctionDescription(bean.getDescription());
                eventPayload.setPrice(bean.getCurrPrice());
                eventPayload.setTimeStamp(new Date());
                bidEvent.fire(eventPayload);
            }

    欠けているインポートを修正し、ファイルを保存します。

    インターセプタのplaceBidメソッドに@Notifyアノテーションを追加します。


    NetBeansは、メソッドがインターセプトされたメソッドであることを示します。

    <interceptors>要素をインターセプタ・クラスの完全修飾名とともに、beans.xmlファイルに追加します。


    インターセプトが有効になります。

    アプリケーションがサーバーに自動的にデプロイされるように、ファイルを保存します。

インターセプタによるAuctionWatcherアプリケーションのテスト

    ブラウザで、URL:localhost:8080/AuctionWatcherを入力して、再びアプリケーションを開き、ウォッチャーの名前をいくつか入力します。

    Itemビュー・ページに移動して、「Bid」ボタンをクリックし、入札を行います。

    GlassFish Serverのコンソール・ウィンドウで、インターセプタが呼び出されて、ウォッチャーに通知するイベントを起動したことを確認します。


    イベント通知コードをビジネス・ロジックから切り離す場合、このアプローチの方がより望ましい方法です。 このアプローチにより、イベントを起動するコードを一元化し、ビジネス・ロジックでコードが重複するのを回避できます。

まとめ

    このチュートリアルで学習した内容は、次のとおりです。

    • CDIイベントを作成する。
    • 特定のタイプをイベントに適用するために、CDI修飾子を作成する。
    • イベントをコードで直接起動する。
    • インターセプタを使用して、コードをさらにイベント・コードから切り離す。

    参考資料

    このチュートリアルのアプリケーションは、JSF FaceletsとマネージドBeanを使用しています。これらのテクノロジーについて詳しくは、次の参考資料を参照してください。

    著者

    • Lead Curriculum Developer:Tom McGinn

このOracle by Exampleをナビゲートする際、次の機能を使用できます。

ヘッダー・ボタンの非表示:
ヘッダー内のボタンを非表示にするには、タイトルをクリックします。ボタンを再表示するには、もう一度タイトルをクリックします。
トピック一覧ボタン:
すべてのトピックの一覧です。いずれかのトピックをクリックすると、その項に移動します。
すべてのトピックを開く/閉じる:
すべての項に対する詳細を表示または非表示にします。デフォルトでは、すべてのトピックが閉じられています。
すべてのイメージを表示/非表示:
すべてのスクリーンショットを表示または非表示にします。デフォルトでは、すべてのイメージが表示されています。
印刷:
コンテンツを印刷します。現在表示または非表示にされているコンテンツが印刷されます。

このチュートリアルの特定の項に移動するには、一覧からトピックを選択してください。