@Alternative Beanおよびライフサイクル注釈の適用

執筆: Andy Gibson

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

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入門パート2 - 注入というタイトルのブログをベースにしています。ここでは、@Alternative注釈を利用して、異なるデプロイメント向けにアプリケーションを構成する方法や、@PostConstruct@PreDestroyなどの管理対象Beanライフサイクル注釈を使用して、CDI注入をJava EE 6管理対象Beanの仕様で提供されている機能と組み合せる方法を示します。

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


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

ソフトウェアまたはリソース 必須バージョン
NetBeans IDE 7.1、7.2、7.3、Java EEバージョン
Java Development Kit (JDK) バージョン6
GlassFishサーバー Open Source Edition 3.1または3.0.1
cdiDemo2.zip 該当なし

注意:

  • NetBeans IDEのJavaバンドル版には、Java EE 6準拠のコンテナであるGlassFish Server Open Source Edition 3.xも含まれています。
  • CDIのサポートは、パッチ1を経由すればNetBeans 6.8でも使用できます。
  • このチュートリアルのサンプル・ソリューション・プロジェクトをダウンロードできます: cdiDemo3.zip

複数デプロイメントの処理

CDIでは、注入ポイントに一致するBeanが複数存在する場合でも、あいまい性のエラーを発生させずにパッケージ化できる@Alternative注釈を使用できます。つまり、2つ以上のBeanに@Alternative注釈を適用してから、デプロイメントに基づいて、使用するBeanをCDIのbeans.xml構成ファイルに指定できます。

これを示すために、次のようなシナリオを考えます。メインItemProcessorクラスにItemValidatorを注入します。ItemValidatorは、DefaultItemValidatorRelaxedItemValidatorの両方によって実装されます。ここでのデプロイメント要件に基づき、ほとんどの場合にDefaultItemValidatorを使用しますが、特定のデプロイメント向けにRelaxedItemValidatorも必要とします。これを解決するために、両方のBeanに注釈を付けてから、アプリケーションのbeans.xmlファイルにエントリを追加して、指定のデプロイメントにどのBeanを使用するかを指定します。

この課題で作成されるオブジェクトを示すCDI図
  1. まず、cdiDemo2.zipファイル(上記の必要なリソースの一覧表を参照)からサンプルのスタート・プロジェクトを抽出します。「ファイル」>「プロジェクトを開く」([Ctrl]-[Shift]-[O]、Macの場合は[⌘]-[Shift]-[O])を選択してから、コンピュータ上のこのプロジェクトの場所を選択することで、IDEでプロジェクトを開きます。
  2. ItemValidatorインタフェースを作成します。

    「新規ファイル」(「新規ファイル」ボタン)ボタンをクリックするか、[Ctrl]-[N] (Macの場合は[⌘]-[N])を押してファイル・ウィザードを開きます。
  3. 「Java」カテゴリから「Javaインタフェース」を選択します。「次」をクリックします。
  4. クラス名として「ItemValidator」、パッケージとして「exercise3」と入力します。
  5. 「終了」をクリックします。新しいインタフェースが生成され、エディタで開かれます。
  6. Itemオブジェクトを取ってboolean値を返すisValid()という名前のメソッドを追加します。
    public interface ItemValidator {
        boolean isValid(Item item);
    }
    (エディタのヒントを使用してexercise2.Itemのインポート文を追加します。)
  7. ItemProcessorクラスを拡張して新しい機能を組み込みます。エディタでItemProcessorを開いて、次のように変更します。
    @Named
    @RequestScoped
    public class ItemProcessor {
    
        @Inject @Demo
        private ItemDao itemDao;
    
        @Inject
        private ItemValidator itemValidator;
    
        public void execute() {
          List<Item>  items = itemDao.fetchItems();
          for (Item item : items) {
              System.out.println("Item = " + item + " valid = " + itemValidator.isValid(item));
          }
        }
    }

    エディタのヒントを使用してexercise3.ItemValidatorのインポート文を追加します。

  8. 値の制限値をテストするのみの、DefaultItemValidatorという名前のItemValidatorの実装を作成します。

    「プロジェクト」ウィンドウで「exercise3」パッケージを右クリックし、「新規」>「Javaクラス」を選択します。クラスに「DefaultItemValidator」という名前を付け、「終了」をクリックします。

  9. 次のようにして、DefaultItemValidatorItemValidatorを実装し、isValid()メソッドをオーバーライドします。
    public class DefaultItemValidator implements ItemValidator {
    
        @Override
        public boolean isValid(Item item) {
            return item.getValue() < item.getLimit();
        }
    }

    (エディタのヒントを使用してexercise2.Itemのインポート文を追加します。)

  10. IDEのメイン・ツールバーにある「プロジェクトの実行」(「プロジェクトの実行」ボタン)ボタンをクリックします。プロジェクトがコンパイルされてGlassFishにデプロイされ、アプリケーションの開始ページ(process.xhtml)がブラウザで開きます。
  11. ページに表示されている「Execute」ボタンをクリックします。IDEに戻ってGlassFishのサーバー・ログを調べます。サーバー・ログは、「出力」ウィンドウ([Ctrl]-[4]、Macの場合は[⌘]-[4])の「GlassFish」タブの下に表示されます。項目が検証されていることが表示されます。制限値より小さい、有効な項目のみが一覧表示されます。
    INFO: Item =  [Value=34, Limit=7] valid = false
    INFO: Item =  [Value=4, Limit=37] valid = true
    INFO: Item =  [Value=24, Limit=19] valid = false
    INFO: Item =  [Value=89, Limit=32] valid = false
    「出力」ウィンドウ - GlassFishサーバー・ログ
  12. ここで、条件を緩和して、値が制限の2倍を超える場合にのみ項目を無効と見なす別のサイトへデプロイするシナリオを考えます。このロジックのために、ItemValidatorインタフェースを実装する別のBeanを用意します。

    RelaxedItemValidatorという名前のItemValidatorの新しい実装を作成します。「プロジェクト」ウィンドウで「exercise3」パッケージを右クリックし、「新規」>「Javaクラス」を選択します。クラスに「RelaxedItemValidator」という名前を付け、「終了」をクリックします。

  13. 次のようにして、RelaxedItemValidatorItemValidatorを実装し、isValid()メソッドをオーバーライドします。
    public class RelaxedItemValidator implements ItemValidator {
    
        @Override
        public boolean isValid(Item item) {
            return item.getValue() < (item.getLimit() * 2);
        }
    }

    (エディタのヒントを使用してexercise2.Itemのインポート文を追加します。)

  14. 「プロジェクトの実行」(「プロジェクトの実行」ボタン)ボタンをクリックして、プロジェクトを実行します。今度はプロジェクトのデプロイに失敗します。
  15. 出力ウィンドウ([Ctrl]-[4]、Macの場合は[⌘]-[4])でサーバー・ログを調べます。「あいまいな依存性」の問題を報告するエラー・メッセージが確認できます。これは、現時点で同じインタフェースを実装しているクラスが2つあるために起こります。
    org.glassfish.deployment.common.DeploymentException: Injection point has ambiguous dependencies.
    Injection point: field exercise2.ItemProcessor.itemValidator;
    Qualifiers: [@javax.enterprise.inject.Default()];
    Possible dependencies: [exercise3.RelaxedItemValidator, exercise3.DefaultItemValidator]

    CDIの実装であるWeldは、特定の注入ポイントにRelaxedItemValidatorDefaultItemValidatorのどちらを使用するかを決定できません。

    前述のように、唯一の違いはデプロイメントに基づいています。ほとんどのデプロイメントにはデフォルトのバリデータを使用しますが、1つのデプロイメントには「緩和された」実装を使用するようにします。CDIでは、注入ポイントに一致するBeanが複数存在する場合でも、あいまい性のエラーを発生させずにパッケージ化できる@Alternative注釈を使用できます。使用するBeanは、beans.xmlに定義します。これにより、beans.xmlの定義のみが異なる両方の実装を同じモジュール内にデプロイできます(この定義はデプロイメントごとに変更できます)。

  16. @Alternative注釈および対応するインポート文を、RelaxedItemValidatorおよびDefaultItemValidatorに追加します。

    エディタでRelaxedItemValidatorを開いて、次のように変更します。
    import javax.enterprise.inject.Alternative;
    ...
    
    @Alternative
    public class RelaxedItemValidator implements ItemValidator {
    
        public boolean isValid(Item item) {
            return item.getValue() < (item.getLimit() * 2);
        }
    }

    @Al」を入力してから[Ctrl]-[Space]を押して、コード補完を呼び出します。1つのオプションのみがフィルタされるため、@Alternative注釈が完了します。また、対応するjavax.enterprise.inject.Alternativeのインポート文がファイルの最初に自動的に追加されます。通常は、注釈で[Ctrl]-[Space]を押すとJavadocドキュメントのポップアップも表示されます。

    エディタでのJavadocドキュメントのポップアップ

    DefaultItemValidatorに切り替え([Ctrl]-[Tab]を押し)、次のように変更します。

    import javax.enterprise.inject.Alternative;
    ...
    
    @Alternative
    public class DefaultItemValidator implements ItemValidator {
    
        public boolean isValid(Item item) {
            return item.getValue() < item.getLimit();
        }
    }

    ここでアプリケーションをデプロイすると「満たされない依存性」というエラーが出ますが、これは、一致するBeanを選択肢として2つ定義したけれども、beans.xmlファイルでどちらも有効にしていないためです。

  17. IDEの「ファイルに移動」ダイアログを使用すると、すばやくbeans.xmlを開けます。IDEのメイン・メニューで「ナビゲート」>「ファイルに移動」([Alt]-[Shift]-[O]、Macの場合は[Ctrl]-[Shift]-[O])を選択してから「beans」と入力します。「OK」をクリックします。 「ファイルに移動」ダイアログ
  18. beans.xmlファイルに以下の変更を加えます。
    <beans xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    
        <alternatives>
            <class>exercise3.RelaxedItemValidator</class>
        </alternatives>
    
    </beans>

    これによって、このデプロイメントではRelaxedItemValidatorを使用することがCDIに伝えられます。@Alternative注釈は、Beanを事実上無効にして注釈に使用できないようにする一方で、他のBeanとともに実装をパッケージ化できるようにするものと考えることができます。これをbeans.xmlファイルに代替として追加すると、事実上、Beanを再度有効にして、注入に使用できるようにします。このタイプのメタデータをbeans.xmlファイルへ移動することで、様々なバージョンのファイルを様々なデプロイメントでバンドルできます。

  19. 「プロジェクトの実行」(「プロジェクトの実行」ボタン)ボタンをクリックして(または[F6]、Macの場合は[fn]-[F6]を押して)、プロジェクトを実行します。ブラウザで、ページに表示されている「Execute」ボタンをクリックします。IDEに戻り、出力ウィンドウ([Ctrl]-[4]、Macの場合は[⌘]-[4])に表示されたGlassFishのサーバー・ログを調べます。
    INFO: Item =  [Value=34, Limit=7] valid = false
    INFO: Item =  [Value=4, Limit=37] valid = true
    INFO: Item =  [Value=24, Limit=19] valid = true
    INFO: Item =  [Value=89, Limit=32] valid = false

    3つ目の項目に、提供された値(24)が指定された制限(19)より大きいにもかかわらず、有効であると表示されています。これにより、RelaxedItemValidator実装が使用されていることがわかります。


管理対象Beansへのライフサイクル注釈の適用

この課題では、メインItemProcessorクラスにItemErrorHandlerを注入します。FileErrorReporterItemErrorHandlerインタフェースの唯一の実装であるため、これが注入用に選択されます。クラスのライフサイクル固有のアクションを設定するには、管理対象Beanの仕様(JSR 316: Java Platform, Enterprise Edition 6の仕様に含まれます)から@PostConstructおよび@PreDestroy注釈を使用します。

この課題で作成されるオブジェクトを示すCDI図

例の続きとして、無効な項目が見つかったときにそれを処理するItemErrorHandlerインタフェースを作成します。

  1. 「プロジェクト」ウィンドウで「exercise3」パッケージを右クリックし、「新規」>「Javaインタフェース」を選択します。
  2. Javaインタフェース・ウィザードで、クラス名として「ItemErrorHandler」、パッケージとして「exercise3」と入力します。「終了」をクリックします。

    新しいインタフェースが生成され、エディタで開かれます。

  3. 引数としてItemオブジェクトを取るhandleItem()という名前のメソッドを追加します。
    public interface ItemErrorHandler {
        void handleItem(Item item);
    }

    (エディタのヒントを使用してexercise2.Itemのインポート文を追加します。)

  4. まず、項目の詳細をファイルに保存するFileErrorReporterという名前の偽のハンドラを持つItemErrorHandlerを実装します。

    「プロジェクト」ウィンドウで「exercise3」パッケージを右クリックし、「新規」>「Javaクラス」を選択します。クラスに「FileErrorReporter」という名前を付け、「終了」をクリックします。

  5. 次のようにして、FileErrorReporterItemErrorHandlerを実装し、handleItem()メソッドをオーバーライドします。
    public class FileErrorReporter implements ItemErrorHandler {
    
        @Override
        public void handleItem(Item item) {
            System.out.println("Saving " + item + " to file");
        }
    }

    (エディタのヒントを使用してexercise2.Itemのインポート文を追加します。)

    項目の処理を始める前にファイルを開き、処理中は開いたままにしてファイルに内容を追加し、処理が終了したらファイルを閉じるようにします。initProcess()およびfinishProcess()メソッドをエラー・レポータBeanに手動で追加することもできますが、そうするとコール元がそれらのクラス固有のメソッドについて知る必要があるため、インタフェースへのコードを作成できなくなります。それらの同じメソッドをItemErrorReporterインタフェースに追加することもできますが、そうするとこのインタフェースを実装するすべてのクラスに、これらのメソッドを不必要に実装する必要があります。かわりに、管理対象Beanの仕様(JSR 316: Java Platform, Enterprise Edition 6の仕様に含まれる)からいくつかのライフサイクル注釈を使用して、Beanライフサイクルの特定の時点でBean上でメソッドをコールできます。Beanが構築され、Beanが持つすべての依存性が注入されると、@PostConstruct注釈付きメソッドがコールされます。同様に、Beanがコンテナによって破棄される直前に@PreDestroy注釈付きメソッドがコールされます。

  6. 次のように、対応する@PostConstructおよび@PreDestroy注釈を持つinit()およびrelease()メソッドを追加します。
    public class FileErrorReporter implements ItemErrorHandler {
    
        @PostConstruct
        public void init() {
            System.out.println("Creating file error reporter");
        }
    
        @PreDestroy
        public void release() {
            System.out.println("Closing file error reporter");
        }
    
        @Override
        public void handleItem(Item item) {
            System.out.println("Saving " + item + " to file");
        }
    }
  7. インポートを修正します。エディタを右クリックして「インポートを修正」を選択するか、[Ctrl]-[Shift]-[I] (Macの場合は[⌘]-[Shift]-[I])を押します。javax.annotation.PostConstructおよびjavax.annotation.PreDestroyのインポート文がファイルの最初に追加されます。
  8. 最後に、新しいItemErrorHandler BeanをItemProcessorに追加します。
    @Named
    @RequestScoped
    public class ItemProcessor {
    
        @Inject @Demo
        private ItemDao itemDao;
    
        @Inject
        private ItemValidator itemValidator;
    
        @Inject
        private ItemErrorHandler itemErrorHandler;
    
        public void execute() {
            List<Item>  items = itemDao.fetchItems();
            for (Item item : items) {
                if (!itemValidator.isValid(item)) {
                    itemErrorHandler.handleItem(item);
                }
            }
        }
    }

    (エディタのヒントを使用してexercise3.ItemErrorHandlerのインポート文を追加します。)

  9. 「プロジェクトの実行」(「プロジェクトの実行」ボタン)ボタンをクリックして(または[F6]、Macの場合は[fn]-[F6]を押して)、プロジェクトを実行します。ブラウザで、ページに表示されている「Execute」ボタンをクリックします。IDEに戻り、出力ウィンドウ([Ctrl]-[4]、Macの場合は[⌘]-[4])に表示されたGlassFishのサーバー・ログを調べます。
    INFO: Creating file error reporter
    INFO: Saving  [Value=34, Limit=7] to file
    INFO: Saving  [Value=89, Limit=32] to file
    INFO: Closing file error reporter

関連項目

異なるアプリケーション・デプロイメントでは、無効な項目の処理のために、項目を却下したり、個々に通知を送ったり、フラグを付けたり、単に出力ファイルに一覧表示したりするなどの、異なるルールを使用することがあります。また、これらを組み合せて使用することも考えられます(例: 注文を却下し、販売担当者に電子メールを送ってから、ファイルに注文を一覧表示する)。この種の多角的な問題の処理に適した方法の1つは、イベントを使用する方法です。このシリーズの最終回はCDIイベントについてです:

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

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