木曜日 8 03, 2006
木曜日 8 03, 2006
Java EE 5のDependency Injection(DI)の基本はJNDIルックアップ(javax.naming.Context#lookup())の代用であることを理解しておきましょう。インジェクション用の全てのアノーテーション(@Resource、@EJB、@WebServiceRef、@PersistenceUnitおよび@PersitenceContext)の定義は全て、以下のようなコードに置き換えることができます。
DataSource ds;
InitialContext ctx = new InitialContext();
ds = (DataSource)ctx.lookup("java:comp/...");
ここで注意しなければならないのは、インジェクション処理がlookup()メソッドに指定するJNDI名は環境コンテキスト名("java:comp/"で始まる論理名)であることです。また、参照するリソースの種類により、JNDI論理名から物理名へのマッピングが必要なものと、そうでないものの2種類に分類されます。この2種類について、インジェクションの流れを簡単に示すと以下のようになります。
以下では、これら2種類のパターンについて、JNDIとDI用アノーテーションの関係に注目して考察します。
物理名マッピングが必要なリソースであっても、実際には多くの場合、デフォルトルールによりJNDI物理名が自動的に決定されるようになっています。しかしながら、このJNDI物理名決定のルールはJava EE 5標準仕様の範囲外になっているため、AppServerベンダ毎に異なる可能性があります。また、デフォルトルールを適用できない場合に物理名を明示する方法もベンダ独自になります。この種類のリソースでは、AppServerベンダに特有の物理名決定のルールを理解し、なるべく物理名の自動マッピングを利用して、ベンダ独自のコンフィギュレーションが増えないようなアプリケーションの作成を意識することが望まれます。
以下の表にJNDI物理名マッピングが必要なリソースの種類と、SJS AppServer 9 (glassfish)の場合の物理名決定のデフォルト・ルールを示します。
| リソース | デフォルトのJNDI物理名(glassfishの場合) |
| EJBリモート参照(スタブ) | リモート・ビジネス・インタフェース:EJB=1:1の場合: リモート・ビジネス・インタフェースのクラス名(パッケージ名含む) それ以外の場合: デフォルトマッピングされないため、物理名の指定が必要 |
| javax.sql.DataSource, javax.jms.ConnectionFactory, javax.jms.QueueConnectionFactory, javax.jms.TopicConnectionFactory, javax.jms.Queue, javax.jms.Topic |
@Resource.name要素と同じ。 もしname要素が省略されたら、「インジェクションされる側のクラス名 + "/" + フィールド名(またはプロパティ名)」 |
まず、@EJBの場合の例です。
package example;
public Foo {
@EJB
com.example.Calc calc; // デフォルトJNDI物理名: "com.example.Calc"
@EJB(name="ejb/Calc")
com.example.Calc calc; // デフォルトJNDI物理名: "com.example.Calc"
}
@EJB.nameを明示する/しないに関わらず、参照するセッション・ビーンのJNDI物理名は不変です。その意味では、@EJBアノーテーションにname要素を明示するのはほとんど意味がありません。
上記のように、JNDI物理名がデフォルトマッピングされるのは、リモート・ビジネス・インタフェースとセッション・ビーン本体が1:1の関係にある場合だけです。セッション・ビーンが複数のビジネス・インタフェースを持つ場合や、1つのビジネスインタフェースを複数のセッション・ビーンが実装している場合は、JNDI物理名を明示する必要があります。ベンダ独自のコンフィギュレーションを避けるには、なるべく以下のような複雑なセッションビーンの構成を避けるようにしましょう。
// デフォルトJNDI物理名:なし
@Stateless
@Remote({Calc1.class, Calc2.class})
public CalcImpl implements Calc1, Calc2 {...}
// デフォルトJNDI物理名:なし
@Stateless
@Remote(Calc.class)
public CalcImpl1 implements Calc {...}
// デフォルトJNDI物理名:なし
@Stateless
@Remote(Calc.class)
public CalcImpl2 implements Calc {...}
次に、@Resourceの例です。
package example;
public Foo {
@Resource
DataSource ds; // デフォルトJNDI物理名: "example.Foo/ds"
@Resource(name="jdbc/CustomerDS")
DataSource ds; // デフォルトJNDI物理名: "jdbc/CustomerDS"
}
@Resourceを使用する場合は、@Resource.nameが環境コンテキスト名になり、その環境コンテキスト名がそのままデフォルトでJNDI物理名になります。ベンダ独自の物理名マッピングを避けるためには、なるべく@Resource.name属性にそのリソースのJNDI物理名と同じ名前を定義した方がいいでしょう。
もしこのような物理名のデフォルトマッピングを利用できない状況になった場合は、以下のいずれかの方法で対処します。以下では、@Resourceの場合についてのみ説明していますが、基本的な設定方法の考え方は@EJBの場合も同様です。
@Resource(name="jdbc/CustomerDS", mappedName="jdbc/sys03/H001DS")
DataSource ds;
mappedName要素そのものは標準仕様ですが、以前のエントリで述べたようにmappedName要素のサポートはベンダに強制されていません。そのため、使用するAppServerのベンダによっては機能しない可能性があることに注意しなければなりません。
<resource-ref>
<res-ref-name>jdbc/CustomerDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
<mapped-name>jdbc/sys03/H001DS</mapped-name>
</resource-ref>
アノーテーションのmappedName要素と同様、デプロイメント記述子の<mapped-name>要素も強制されない仕様であるため、ポータビリティに不安があることに注意しなければなりません。
J2EE 1.4仕様までの場合と同様に、ベンダ独自のデプロイメント記述子に環境コンテキスト名とJNDI物理名とのマッピングを定義する方法です。glassfishの場合は、sun-ejb-jar.xml、sun-web.xml、sun-application-client.xmlなどに以下のように定義します。
<resource-ref> <res-ref-name>jdbc/CustomerDS</res-ref-name> <jndi-name>jdbc/jdbc/sys03/H001DS</jndi-name> </resource-ref>
いずれの方法もポータビリティという意味でいまいちですが、ソースコードにJNDI物理名が埋め込まれるのはやはり好ましくありません。もしJNDI物理名のデフォルトマッピングが使用できない状況になった場合は、上記の2.か3.の方法で対処するのがよいと思います。
JNDI物理名マッピングが不用なリソースのほとんどの場合は、@Resource.name要素を省略して定義することが多いでしょう。
package com.example;
@Stateless
public class Calc {
@Resource SessionContext ctx;
@Resource int maxRetry = 3;
@Resource UserTransaction tx;
:
}
もし、@Resource.name要素を省略したまま、デプロイメント記述子を用いてリソースの設定を上書きしたい場合は、リソースの参照の定義に<injection-target>を加えてデプロイメント記述子を定義します。
<env-entry>
<env-entry-name>com.example.CalcImpl/maxRetry</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>10</env-entry-value>
<injection-target>
<injection-target-class>com.example.CalcImpl</injection-target-class>
<injection-target-name>maxRetry</injection-target-name>
</injection-target>
</env-entry>
上記の設定をejb-jar.xmlに含めると、CalcImplクラスのプロパティmaxRetryに設定される値が3から10に変更になります。
cool
Posted by wow gold on 11月月 03日, 2008年 at 10:38 午前 JST #