このブログをフォローしている場合は、CDI( Contexts and Dependency Injection )について最近書いている(そして話す )ことを覚えておいてください。 CDIにはさまざまな側面がありますが、これまでのところ、環境でCDIを開始する方法と、 CDIを既存のJava EE 6アプリケーションに統合する方法に焦点を当て、CDIでの依存性注入に焦点を当てました。 これはCDIでの実装に関する3番目の投稿です。 最初はデフォルトの実装と修飾子について、 2番目はすべての可能な実装ポイント(フィールド、コンストラクタ、セッター)について説明しました。 この投稿では、 プロデューサー、または「 タイプセーフな方法で何でもどこでも実装できる方法 」 について説明します 。
Beanのみをデプロイしますか?
その前に、通常の@Inject
アノテーションを使用して@Inject
を注入する方法を示しました。 先ほど使用した書籍番号の生成の例を見ると、 NumberGenerator
インターフェースの実装が実装されているサーブレットとレストサービスが表示されます。 修飾子のおかげで、サーブレットは実装ポイントで@ThirteenDigit
IsbnGenerator
マークされたIsbnGenerator
を受け取ることができ、残りのサービスはIssnGenerator
マークされた@EightDigits
を受け取ります(私の最初の投稿を参照)。 提示されたクラス図は、Beanインジェクションのいくつかの組み合わせを示しています。
しかし、ご覧のとおり、これはすべて他のBeanへのBeanの注入です 。 CDIでのみBeanを実装できますか? いや 何でもどこでも実装できます 。
プロデューサー
はい、何でもどこでも実装できます。 これのためにしなければならないことは、後で実装したいものを作成することです。 CDI( Factoryパターンの優れた実装)には、このためのプロデューサーがいます。 彼らの助けを借りて、次のものを作成できます。
- クラス:Beanの数に制限のないタイプ、そのスーパークラス、および実装するすべてのインターフェース
- インターフェース:無制限の数のBeanタイプ、その拡張インターフェース、および
java.lang.Object
- プリミティブとJava配列
したがって、 java.util.Date
、 java.lang.String
またはint
埋め込むこともできjava.lang.String
。 いくつかのタイプのデータとプリミティブを作成して実装することから始めましょう。
データ型とプリミティブ型の作成
何でもどこにでも埋め込む方法の1つの例は、データ型とプリミティブの実装です。 それでは、 String
とint
を実装してみましょう。 これを行うには、モデルにクラスを追加する必要があります。 これに先立ち、 IsbnGenerator
の形式で乱数を作成しました。 この番号は、接頭辞( 13-124 )として機能する文字列と、整数値-接尾辞( 4 )で構成されます。 次のクラス図は、数値を生成するために使用されるPrefixGenerator
とPostfixGenerator
2つの新しいクラスを示しています。
たとえば、 PrefixGenerator
のコードを見ると、メソッドとは対照的に、修飾子でマークされていないクラスが表示されます。 getIsbnPrefix
メソッドは、 @ThirteenDigits
アノテーションで指定された文字列を返します。 この行は、CDIを使用して作成されます( @ThirteenDigits
)。 @ThirteenDigits
とその修飾子( @ThirteenDigits
)を使用して埋め込むことができます。
public class PrefixGenerator { @Produces @ThirteenDigits public String getIsbnPrefix() { return "13-84356"; } @Produces @EightDigits public String getIssnPrefix() { return "8"; } }
次に、 PostfixGenerator
クラスを注意深く見てください。 このコードは前のコードと似ていますが、組み込み可能なint
作成する点が異なります。
public class PostfixGenerator { @Produces @ThirteenDigits public int getIsbnPostfix() { return 13; } @Produces @EightDigits public int getIssnPostfix() { return 8; } }
次に、ISBN番号を生成するロジックを変更しましょう。 @Inject
と@ThirteenDigits
を使用して、 String
とint
が@Inject
埋め込まれます。 これで、CDIでの厳密な型指定の意味が理解できました。 この構文( @Inject @ThirteenDigits
)を使用して、CDIはString
代わりに埋め込む必要があるもの、intのNumberGenerator
もの、およびNumberGenerator
実装をNumberGenerator
。
@ThirteenDigits public class IsbnGenerator implements NumberGenerator { @Inject @ThirteenDigits private String prefix; @Inject @ThirteenDigits private int postfix; public String generateNumber() { return prefix + "-" + Math.abs(new Random().nextInt()) + "-" + postfix; } }
プロデューサーフィールドを介したJava EEリソースのデプロイ
それで、CDIが文字列と整数を埋め込むことができるとしても、CDIが何でもどこにでも埋め込むことができるなら、Java EEリソースはどうでしょうか? これは別の話です。プロデューサーはこれを手伝ってくれます。 前述したように、CDIではすべてがタイプセーフであるため、CDI にJNDI名でリソースを埋め込む方法はありません。たとえば、 @Inject(name="jms/OrderQueue")
ます。 標準的な例は、エンティティマネージャです。 CDIを使用していない場合、Java EE 6では次のように実装できます。
@Stateless public class ItemEJB { @PersistenceContext(unitName = "cdiPU") private EntityManager em; ... }
それでは、エンティティマネージャに対して通常の@Inject
を作成できないのはなぜですか? 実装のあいまいさに関する最初の投稿を覚えているなら、同じ問題です。 いくつかの永続性ユニット(通常の文字列と呼ばれる)を持つことができ、単に@Inject
記述すると、CDIはどのユニットを実装する必要があるかを判断できません。 代わりに、エンティティマネージャーを最初に作成し、なんとか名前を付ける必要があります( @Default
を使用したくない場合):
public class DatabaseProducer { @Produces @PersistenceContext(unitName = "cdiPU") @BookStoreDatabase private EntityManager em; }
DatabaseProducer
クラスは@PersistenceContext
を使用して、cdiPU永続性ユニットでエンティティマネージャーを実装します。 指定子( @BookStoreDatabase
)でマークされ、初期化されます。その後、次のようにEJBに埋め込むことができます。
@Stateless public class ItemEJB { @Inject @BookStoreDatabase private EntityManager em; ... }
別の良い例は、JMSファクトリーと宛先の作成です。 @Resource
使用すると、JMSファクトリまたは送り先へのJNDIリンクを取得でき、 @Order
がそれに名前を付け、 @Produces
使用して後で実装できます。
public class JMSResourceProducer { @Produces @Order @Resource(name = "jms/OrderConnectionFactory") private QueueConnectionFactory orderConnectionFactory; @Produces @Order @Resource(name = "jms/OrderQueue") private Queue orderQueue; }
これで、EJBはタイプセーフな@Inject
使用できます。
@Stateless public class ItemEJB { @Inject @Order private QueueConnectionFactory orderConnectionFactory; @Inject @Order private Queue orderQueue; ... }
プロデューサーメソッドを使用したJava EEリソースの作成
考えられる例は非常に単純です。フィールドを作成してから、それを実装できます。 これは生産フィールドと呼ばれます。 ただし、Beanを作成するためにより複雑なビジネスロジックが必要になる場合があり、これをプロデューサーメソッドと呼びます。 別の例を見てみましょう。 JMSメッセージを送信する必要がある場合は、メッセージを送信するまで、常にJMSファクトリーと宛先(キューまたはトピック)を実装し、接続を作成してからセッションなどを作成します。 このコードは頻繁に繰り返され、エラーが含まれる可能性があるため、別のクラスのどこかに置いて、メッセージの送信に必要なセッションや他のコンポーネントを作成するために使用してみませんか? このクラスは次のようになります。
public class JMSResourceProducer { @Resource(name = "jms/OrderConnectionFactory") private QueueConnectionFactory orderConnectionFactory; @Produces @Order @Resource(name = "jms/OrderQueue") private Queue orderQueue; @Produces @Order public QueueConnection createOrderConnection() throws JMSException { return orderConnectionFactory.createQueueConnection(); } @Produces @Order public QueueSession createOrderSession(@Order QueueConnection conn) throws JMSException { return conn.createQueueSession(true, Session.AUTO_ACKNOWLEDGE); } }
まず、クラスは@Resource
を使用してQueueConnectionFactory
およびQueue
へのリンクを取得します。 上記で説明したのと同じ理由で、複数のJMSファクトリーと宛先があり、それらをJNDI名で区別する必要があります。 また、CDIでは注入ポイントで名前を指定できないため、 @Inject
ではなく@Resource
を使用する必要があります。 createOrderConnection
メソッドはQueueConnectionFactory
を使用してQueueConnectionFactory
を作成し、さらに(それを@Order
に@Order
て)実装します。 createOrderSessionメソッドをよく見ると、そのパラメーターはQueueConnection
を実装するために以前に作成されたものQueueConnection
あり、その助けを借りてQueueSession
が作成されていることがわかります。 その結果、JMSセッションを作成せずに外部コンポーネントに埋め込むのは非常に簡単です。
@Stateless public class ItemEJB { @Inject @Order private QueueSession session; @Inject @Order private Queue orderQueue; private void sendOrder(Book book) throws Exception { QueueSender sender = session.createSender(orderQueue); TextMessage message = session.createTextMessage(); message.setText(marshall(book)); sender.send(message); } ... }
作成と廃棄
「わかりました、大まかな作業を行い、接続とセッションを作成するための外部クラスがあります ... しかし、誰がそれらを閉じますか? 」 そしてこの時点で、CDIはさらに魔法を示します: @Disposes
。
public class JMSResourceProducer { @Resource(name = "jms/OrderConnectionFactory") private QueueConnectionFactory orderConnectionFactory; @Produces @Order @Resource(name = "jms/OrderQueue") private Queue orderQueue; @Produces @Order public QueueConnection createOrderConnection() throws JMSException { return orderConnectionFactory.createQueueConnection(); } @Produces @Order public QueueSession createOrderSession(@Order QueueConnection conn) throws JMSException { return conn.createQueueSession(true, Session.AUTO_ACKNOWLEDGE); } public void closeOrderSession(@Disposes @Order QueueConnection conn) throws JMSException { conn.close(); } public void closeOrderSession(@Disposes @Order QueueSession session) throws JMSException { session.close(); } }
CDIにリソースを閉じるように依頼するには、このリソースを作成したメソッド( createOrderSession(@Order QueueConnection conn)
がセッションを作成し、 closeOrderSession(@Order QueueConnection conn)
を閉じて閉じるcloseOrderSession(@Order QueueConnection conn)
に類似したメソッドを定義し、 @Disposes
アノテーションを追加する@Disposes
です。 CDIは、正しい順序でリソースをリサイクルします(最初にセッション、次に接続)。 これについては言及しませんでしたが、CDIはその範囲(要求、セッション、アプリケーション、会話など)に応じてリソースを作成および利用します。 しかし、その別の時間についての詳細。 (情報は、 Java EE 7の始まり [ 翻訳 ]-約です。)
おわりに
以前の投稿( パート1 、 パート2 )から理解したように、CDIは依存性注入に関するものです。 これまで、Beanを注入する方法を示しましたが、どこでも(文字列、プリミティブ、エンティティマネージャー、JMSファクトリー...)どこでも(POJO、サーブレット、EJBなど)を実装する方法を知っています。 実装するものを作成するだけです(プロデュースフィールドまたはプロデュースメソッドのいずれかを使用)。
ソースコード
コードをダウンロードして、あなたがそれについてどう思うか教えてください。