理系学生日記

おまえはいつまで学生気分なのか

CDIにおけるProducerメソッド

JavaEE 良くわかっていないのですが、それでもプロジェクトが JavaEE 標準だーウオーという状況っぽいので、ちょっと勉強をしはじめております。 ぼくが Java に対して得意意識を持てないのは、こういうところの知識がないのもあるので、そのへんを払拭していこうとがんばらなければ。

今日ははじめて、@Produces というアノテーションを知りました。

@Produces

DI というと、今までずっと「コンテナがオブジェクトを注入する際、どのクラスのオブジェクトを注入するのかはコンテナが決定する」と思ってたんですよね。 開発者は、コンテナに対して型情報といわゆる限定子(非推奨みたいだけど @Named とか、@Qualifier を使って作成したものとか)を教えることで、何を注入してほしいのかを伝えるものだと。

じつはそのあたりには別解があって、コンテナではなく、開発者自身がオブジェクトを生成し、それを注入するようにできる方法があります。 それが Producer メソッドと呼ばれるメソッドをつくる方法で、Producer メソッドの作り方というのが、@Produces アノテーションをメソッドに付与することだと。

Producer メソッドを作ると、DI コンテナが注入対象のフィールドだったり引数だったりを見つけると、その注入対象の型を返却してくれる Producer メソッドを呼んでくれるようになります。

このため、Producer メソッドで注入すべきオブジェクトを生成して、それを返却してやるだけで、動的に注入するオブジェクトのクラスを切り替えたりもできるようになると。なるほど便利だ。

このように、Producer メソッドでは、実行時に注入する型を変える他にも

  • Bean ではないようなオブジェクトを注入する
  • 注入するオブジェクトに対して、コンストラクタでの初期化以外のカスタマイズを実行する

といった特徴を持たせることができます。 百聞は一見にしかずなのですが、Weld 5.0.0.CR1 - CDI Reference Implementation に Producer メソッドで、プリミティブとしての int を生成し注入するための Producer メソッドの実装例がありました。

import javax.enterprise.inject.Produces;


@ApplicationScoped
public class RandomNumberGenerator {


   private java.util.Random random = new java.util.Random(System.currentTimeMillis());

   @Produces @Named @Random int getRandomNumber() {
      return random.nextInt(100);
   }
}

こちらは、Strategy パターンの型を、動的に切り替える例

import javax.enterprise.inject.Produces;

@SessionScoped
public class Preferences implements Serializable {

   private PaymentStrategyType paymentStrategy;

   ...

   @Produces @Preferred
   public PaymentStrategy getPaymentStrategy() {

       switch (paymentStrategy) {
           case CREDIT_CARD: return new CreditCardPaymentStrategy();
           case CHECK: return new CheckPaymentStrategy();
           case PAYPAL: return new PayPalPaymentStrategy();
           default: return null;
       }
   }
}

たいへんに、なるほど〜〜〜感あります。

ただ、これを多用すると、DI における依存性解決がコードから読み取りにくくなるので、まぁ用法用量を守ってお使いくださいという感じなんでしょう。