CDIのイベントの操作

執筆: Andy Gibson

このページの内容は、NetBeans IDE 7.2、7.3、7.4および8.0に適用されます

JSR-299で指定されているコンテキストと依存性の注入(CDI: Contexts and Dependency Injection)はJava EE 6の不可欠な部分であり、サーブレット、エンタープライズBean、JavaBeansなどのJava EEコンポーネントが、アプリケーションのライフサイクル内で明確なスコープを持って存在できるようにするためのアーキテクチャを提供します。また、CDIサービスによって、EJBセッションBeanやJSF (JavaServer Faces)管理対象BeanなどのJava EEコンポーネントが注入可能になり、イベントの起動や監視による疎結合方式の対話が可能になります。

このチュートリアルは、Andy Gibson氏によって投稿されたCDI入門パート3 - イベントというタイトルのブログをベースにしています。ここでは、Java EEのイベントの概念を活用する方法を示します。この方法は、アプリケーション内のイベントの生成およびイベントへのサブスクライブ(つまり監視)について、プロデューサとオブザーバの間でコードを分離して管理できます。javax.enterprise.event.Eventクラスを使用してイベントを作成し、CDIの@Observes注釈を使用してイベントにサブスクライブします。

NetBeans IDEは、コンテキストと依存性の注入のサポートを組込みでサポートしています。これには、プロジェクト作成時にbeans.xml CDI構成ファイルを生成するオプション、注釈のためのエディタおよびナビゲーション・サポート、一般的に使用されるCDIアーティファクトを作成するための各種ウィザードなどが含まれています。


このチュートリアルを完了するには、次のソフトウェアとリソースが必要です。

ソフトウェアまたはリソース 必須バージョン
NetBeans IDE 7.2、7.3、7.4、8.0、Java EEバージョン
Java Development Kit (JDK) バージョン7または8
GlassFishサーバー Open Source Edition 3.xまたは4x
cdiDemo3.zip n/a

注意:

  • NetBeans IDEのJava EEバンドル版には、Java EE準拠のコンテナであるGlassFish Server Open Source Editionも含まれています。
  • このチュートリアルのサンプル・ソリューション・プロジェクトをダウンロードできます: cdiDemoComplete.zip

イベントの利用

前の@Alternative Beanおよびライフサイクル注釈の適用のチュートリアルでは、項目の一覧を取得してそれを検証し、無効な項目が見つかったら特定のアクションを起こすアプリケーションを作成しました。仮に、今後システムを拡大して、無効な項目が見つかった場合に発生するあらゆることを処理できるようにするとします。これには、電子メールの送信、他のデータの変更(注文の取消しなど)、またはファイルやデータベース表への却下リストの格納など、様々なものがあります。実装を完全に分離するために、Java EEのイベントを使用できます。イベントは、イベント・プロデューサによって生成され、イベント・オブザーバからサブスクライブされます。ほとんどのCDIと同様にイベントの生成およびサブスクライブは型保証であるため、修飾子は、監視するイベント・オブザーバを判別できます。

このシリーズで前のチュートリアルからビルドしているアプリケーションを使用した場合、それほど多くの変更をしなくてもこの実装が可能です。項目を処理するたびにイベントを生成する、もう1つのItemErrorHandler (前のチュートリアルで作成)の実装を提供するのみです。このクラスにEventItemHandlerという名前を付けてItemProcessorに注入します。注入対象を選択するには、Notify修飾子を使用します。

この課題で作成されるオブジェクトを示すCDI図
  1. まず、cdiDemo3.zipファイル(上記の必要なリソースの一覧表を参照)からサンプルのスタート・プロジェクトを抽出します。「ファイル」>「プロジェクトを開く」([Ctrl]-[Shift]-[O]、Macの場合は[⌘]-[Shift]-[O])を選択してから、コンピュータ上のこのプロジェクトの場所を選択することで、IDEでプロジェクトを開きます。
  2. EventItemHandlerという名前のクラスを作成します。「新規ファイル」(「新規ファイル」ボタン)ボタンをクリックするか、[Ctrl]-[N] (Macの場合は[⌘]-[N])を押してファイル・ウィザードを開きます。
  3. 「Java」カテゴリから「Javaクラス」を選択します。「次」をクリックします。
  4. クラス名として「EventItemHandler」、パッケージとして「exercise4」と入力します。
  5. 「終了」をクリックします。新しいクラスおよびパッケージが生成され、エディタでクラスが開きます。
  6. 次のようにしてEventItemHandlerを実装します。
    public class EventItemHandler implements ItemErrorHandler {
    
        @Inject
        private Event<Item> itemEvent;
    
        @Override
        public void handleItem(Item item) {
            System.out.println("Firing Event");
            itemEvent.fire(item);
        }
    }
    イベント・ペイロードがItemになるEventのインスタンスを注入します。イベント・ペイロードとはイベント・プロデューサからイベント・オブザーバに渡される状態データのことで、この場合は却下されたItemが渡されます。無効な項目が処理されたら、イベントを起動して、受け取った無効な項目を渡します。このイベント・ベースの項目ハンドラは、他の項目ハンドラと同じように注入されるため、いつでも必要なときに交換したり、テスト中に取り換えたりできます。
  7. すべてのインポートを修正します。エディタを右クリックして「インポートを修正」を選択するか、[Ctrl]-[Shift]-[I] (Macの場合は[⌘]-[Shift]-[I])を押します。必ずEventクラスの完全修飾名としてjavax.enterprise.event.Eventを選択するようにしてください。
    「インポートを修正」ダイアログ

    Eventの上で[Ctrl]-[Space]を押して、クラスのJavadoc定義を表示します。上記で使用したfire()メソッドも定義されています。
    エディタのJavadocポップアップ
  8. Notifyという名前の修飾子を作成します。(修飾子についてはCDIの注入および修飾子の操作に記載。)
  9. 「新規ファイル」(「新規ファイル」ボタン)ボタンをクリックするか、[Ctrl]-[N] (Macの場合は[⌘]-[N])を押してファイル・ウィザードを開きます。
  10. 「コンテキストと依存性の注入」カテゴリから「修飾子タイプ」を選択します。「次」をクリックします。
  11. クラス名として「Notify」、パッケージとして「exercise4」と入力します。
  12. 「終了」をクリックします。新しいNotify修飾子がエディタで開きます。
    @Qualifier
    @Retention(RUNTIME)
    @Target({METHOD, FIELD, PARAMETER, TYPE})
    public @interface Notify {
    }
  13. EventItemHandler@Notify注釈を追加します。
    @Notify
    public class EventItemHandler implements ItemErrorHandler {
    
        ...
    }
    このエラー・ハンドラを注入のために識別し、ItemProcessorで注入ポイントに追加して使用できる@Notify修飾子注釈を作成しました。
  14. exercise2.ItemProcessorで、EventItemHandlerの注入ポイントに@Notify注釈を追加します。
    @Named
    @RequestScoped
    public class ItemProcessor {
    
        @Inject @Demo
        private ItemDao itemDao;
    
        @Inject
        private ItemValidator itemValidator;
    
        @Inject @Notify
        private ItemErrorHandler itemErrorHandler;
    
        public void execute() {
            List<Item> items = itemDao.fetchItems();
            for (Item item : items) {
                if (!itemValidator.isValid(item)) {
                    itemErrorHandler.handleItem(item);
                }
            }
        }
    }
    (エディタのヒントを使用してexercise4.Notifyのインポート文を追加します。)
  15. 「プロジェクトの実行」(「プロジェクトの実行」ボタン)ボタンをクリックして、プロジェクトを実行します。
  16. ブラウザで「Execute」ボタンをクリックしてからIDEに戻り、「出力」ウィンドウ([Ctrl]-[4]、Macの場合は[⌘]-[4])でサーバー・ログを調べます。ビルドしてきたアプリケーションは、現時点でDefaultItemDaoを使用して4つのItemを設定してからItemRelaxedItemValidatorを適用するため、itemErrorHandlerが2度起動するのが確認できるはずです。
    「出力」ウィンドウ - GlassFishサーバー・ログ
    しかし、現時点ではイベントを監視しているものはありません。これは、@Observes注釈を使用してオブザーバ・メソッドを作成すれば修正できます。イベントを監視するために必要な手順はこれのみです。これを示すため、FileErrorReporter (前のチュートリアルで作成)にこのhandleItem()メソッドをコールするオブザーバ・メソッドを追加して、起動されたイベントに応答するように変更できます。
  17. FileErrorReporterがイベントに応答するようにするには、クラスに次のメソッドを追加します。
    public class FileErrorReporter implements ItemErrorHandler {
    
        public void eventFired(@Observes Item item) {
            handleItem(item);
        }
    
        ...
    }
    (エディタのヒントを使用してjavax.enterprise.event.Observesのインポート文を追加します。)
  18. 再びプロジェクトを実行([F6]、Macの場合は[fn]-[F6])し、「Execute」ボタンをクリックしてからIDEに戻り、「出力」ウィンドウでサーバー・ログを調べます。
    「出力」ウィンドウ - GlassFishサーバー・ログ
    先ほどと同じく無効なオブジェクトでイベントが起動されますが、今度は各イベントの起動時に項目の情報が保存されるようになったのが確認できます。また、起動されたイベントごとにFileErrorReporter Beanが作成されて閉じられているため、ライフサイクル・イベントが監視されていることもわかります。(@PostConstruct@PreDestroyなどのライフサイクル注釈については、@Alternative Beanおよびライフサイクル注釈の適用を参照。)

上記の手順で示したように、@Observes注釈はイベントを監視するための簡単な方法を提供します。

イベントおよびオブザーバは、修飾子を使用して注釈を付けることによって、オブザーバが項目の特定のイベントのみを監視するようにもできます。デモについては、CDI入門パート3 – イベントを参照してください。


スコープの処理

現状のアプリケーションでは、イベントが生成されるたびにFileErrorReporter Beanが作成されます。この場合、項目ごとにファイルを開いて閉じる必要はないため、毎回新しいBeanを作成することは望ましくありません。ただし、プロセスの開始時にファイルを開き、プロセスの完了時にファイルを閉じる必要があります。このために、FileErrorReporter Beanのスコープについて考慮する必要があります。

現時点では、FileErrorReporter Beanに定義されたスコープはありません。定義されたスコープがない場合、CDIはデフォルトの依存擬似スコープを使用します。これは実際のところ、Beanが非常に短い期間(通常はメソッド・コールの期間)で作成および破棄されることを意味します。現在のシナリオでは、起動されたイベントの期間でBeanが作成および破棄されます。これを修正するために、手動でスコープ注釈を追加してBeanのスコープを延ばすことができます。このBeanに@RequestScopedを指定して、最初のイベント起動時にBeanが作成されたら、リクエストの期間存在し続けるようにします。これはまた、このBeanを注入できるどの注入ポイントにおいても、同じBeanインスタンスが注入されることを意味します。

  1. FileErrorReporterクラスに、@RequestScope注釈および対応するjavax.enterprise.context.RequestScopedのインポート文を追加します。
    import javax.enterprise.context.RequestScoped;
    ...
    
    @RequestScoped
    public class FileErrorReporter implements ItemErrorHandler { ... }
    入力中に[Ctrl]-[Space]を押すと、エディタのコード補完サポートを呼び出せます。コード補完で項目を選択すると、関連付けられたすべてのインポート文が自動的にクラスに追加されます。
    エディタのコード補完ポップアップ
  2. 再びプロジェクトを実行([F6]、Macの場合は[fn]-[F6])し、「Execute」ボタンをクリックしてからIDEに戻り、「出力」ウィンドウでサーバー・ログを調べます。
    「出力」ウィンドウ - GlassFishサーバー・ログ
    FileErrorReporter Beanが最初のイベントの起動時にのみ作成され、最後のイベントの起動後に閉じられます。
    INFO: Firing Event
    INFO: Creating file error reporter
    INFO: Saving  [Value=34, Limit=7] to file
    INFO: Firing Event
    INFO: Saving  [Value=89, Limit=32] to file
    INFO: Closing file error reporter
    

システムの各部分をモジュール式で分離するには、イベントの使用をお薦めします。イベントを使用すると、イベントのオブザーバとプロデューサは互いのことを意識する必要がなくなり、そのための構成の必要もなくなります。イベントのプロデューサにオブザーバを意識させることなく、イベントにサブスクライブするコード部分を追加できます。(イベントを使用しない場合、通常は手動でイベントのプロデューサにオブザーバをコールさせる必要があります。)たとえば、だれかが注文ステータスを更新したら営業担当に電子メールを送るイベントや、技術サポートの問題が未解決のまま1週間を超えたらアカウント・マネージャに通知するイベントを追加できます。このような種類のルールはイベントを使用しなくても実装できますが、イベントを使用するとビジネス・ロジックを簡単に分離できるようになります。さらに、コンパイル時やビルド時の依存性がなくなります。ただアプリケーションにモジュールを追加するのみで、自動的にイベントの監視および生成が始まります。


関連項目

CDIおよびJava EEの詳細は、次のリソースを参照してください。

NetBeansリソース

外部リソース

get support for the NetBeans

Support


By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2013, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo