Takashi Nishigaya
Nishigaya's Weblog
Profile
Takashi Nishigaya
Sr. Java Architect
SunJava Consulting
Professional Services Delivery
アーカイブ
« 11月 2009
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
     
       
今日
Click me to subscribe
Search

Java.net
リンク
 
« Java EE 5:デプロイメント記述子... | メイン | Java EE 5:Dependency... »
火曜日 7 25, 2006
Java EE 5: Dependency Injection, XML or Annotation?

Java EE 5では、Spring FrameworkSeasar2でよく知られる「依存性の注入(DI: Dependency Injection)」の機能が加わりました。DIはPOJOベースのアプリケーション・デザインを支える基盤技術の一つです。DIを効率よく使用すると以下のようなメリットがあります。

DIの機能を利用するためには、コンポーネント間結合のコードを書く代わりに、DIのためのコンフィギュレーション(メタ情報)をDIコンテナに与えてあげます。DIのためのメタ情報をコンテナに伝える手段はDIコンテナによって異なりますが、一般にはXML形式の外部ファイルやソースコードに埋め込むアノーテーションなどが使われます。例えば、Spring FrameworkにおけるワイヤリングはXMLファイルであり、Searsar2ではXMLに加えコンベンションによるDIをサポートしています。一方、Java EE 5の場合はどうでしょうか。Java EE 5のDIはアノーテーションで定義するものだと思っている方が多いと思います。しかし、実はJava EE 5のDIコンフィギュレーションは、アノーテーションを定義した場合と同等のメタ情報をXMLの外部ファイル(デプロイメント記述子)でも定義することが可能です。

例えば、「Java EE 5チュートリアル:EJB3 セッション・ビーン編(2)」で紹介したように、セッション・ビーンの参照をクライアントにインジェクションするには、以下の様に@EJBアノーテーションを使用しました。

public class Client {

    @EJB static Calc calc;
         :
}

これと全く同じメタ情報をXML外部ファイルとして定義するためには、デプロイメント記述子(クライアント・コンテナの場合はMETA-INF/application-client.xml)にCalcビーンのための<ejb-ref>要素の定義とそれをインジェクションする場所を<injection-target>要素で定義してあげます。

<application-client version="5"
    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/application-client_5.xsd">
  <ejb-ref>
    <ejb-ref-name>Client/calc</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <remote>com.example.Calc</remote>
    <injection-target>
      <injection-target-class>Client</injection-target-class>
      <injection-target-name>calc</injection-target-name>
    </injection-target>
  </ejb-ref>
</application-client>

上記の例では、クラスCalcのメンバ変数calc(またはsetterメソッドsetCalc(Calc))に環境コンテキスト名java:comp/env/Client/calcのEJBをインジェクションすることを定義しています([1], [2])。この定義があれば、Clientクラスの@EJBアノーテーションを削除してもインジェクションは適切に行なわれます。実際に動作を確認したい場合は、以前のエントリ「Java EE 5チュートリアル:EJB3 セッション・ビーン編(2)」のClient.javaと同じディレクトリで以下の作業を行なって確認してみて下さい。

$ mkdir META-INF
$ vi META-INF/application-client.xml                    # エディタで上記内容のxmlを作成
$ vi Client.java                                        # エディタで@EJBを削除
$ javac -classpath $ASROOT/lib/javaee.jar:. Client.java # コンパイル
$ $ASROOT/bin/appclient Client                          # 実行
10 + 20 = 30

@EJBに限らず、Java EE 5環境で利用可能な全てのインジェクション用のアノーテション(@EJB、@WebServiceRef、@Resource、@PersistenceUnit、@PersitenceContext)はデプロイメント記述子でも同等のメタ情報を定義することが可能です。それでは、インジェクションのコンフィギュレーションには、アノーテーションを使うべきでしょうか?それとも設定ファイルとしてのデプロイメント記述子を使うべきでしょうか?その答えを決めるには、コンポーネントの再利用のライフサイクルとソースコードのコンパイルのコストを考慮する必要があります。

コンポーネントの再利用には短期的な観点と長期的な観点があります。短期的な再利用とは、例えば、あるコンポーネントをスタンドアロン環境で単体テストする場合と本来のJava EEコンテナ上で動作させる場合に、そのコンポーネントに依存するオブジェクトの実体(環境)を変更することです。Java EE 5のインジェクションは(テストのやり方にもよりますが)Java EEコンテナ上でなければ機能しないのですから、単体テストでは依存関係にあるオブジェクトをテストコードで直接設定してあげればよいため、テスト対象のコンポーネントにインジェクション用アノーテーションがあっても邪魔にはなりません。また、開発中はコンポーネント間の依存関係が頻繁に代わる可能性がありますが、開発中はコンパイルと実行を頻繁に行なうため、アノーテーションを含めたソースコード修正のコストはそれほど大きくありません。開発者にとっては、デプロイメント記述子に注意を払うよりは、ソースコードに注意を払う方が作業効率としても高いでしょう。従って、短期的な観点ではアノーテーションを積極的に使用する方がよいと考えられます。

長期的なコンポーネントの再利用の観点では、あるコンポーネントが使用している別のコンポーネントの実装が変更になったり、コンポーネントを動作させるプラットフォームそのものをマイグレーションさせる必要が生じたりする場合に、そのコンポーネントのソースに全く(あるいはほとんど)手を加えることなく動作させたいと考えます。あるいは、JNDI名の物理名を変更するだけであったり、@Resourceで指定した初期設定の値を変更するだけよい場合もあります。この場合の再利用のライフサイクルは数カ月から数年の期間が想定されますので、担当の開発者が代わっていることも多いでしょう。このような場合には、ソースコードには手を加えずアノーテーションの定義はそのままで、デプロイメント記述子の追加・変更で対応するのがよいと思われます。

このように、基本的にはインジェクション用のアノーテーションは積極的に使用することが推奨されますが、1つ注意しておきたい点があります。それは、アノーテーションを定義する際に、デフォルトをなるべく変更しない(すなわち、不必要にアノーテーションのメンバ要素を明示しない)ということです[3]。特に物理アドレスを示すものに関しては、アノーテーションのメンバ要素には明示しない方がいいでしょう。例えば、あるコンポーネントのアノーテーションにJNDI物理アドレスを示すmappedName要素が明示してあったとしましょう。しばらくしてから、このJNDI物理名を変更する必要がでてきた時、ソースコードのmappedNameを修正せずに、デプロイメント記述子に新しいJNDI物理名を定義して対応することにしたとします。Java EE 5仕様では、アノーテーションとデプロイメント記述子の両方に同じ意味の定義があった場合、ランタイムの動作としてはアノーテーションよりもデプロイメント記述子の定義が優先されることになっていますので、デプロイメント記述子の修正だけで問題ありません。しかし、アノーテーションとデプロイメント記述子で異なる設定が重複して存在するのは気持ちが悪いだけでなく、混乱を招く恐れがあります。そのような状況を避けるためにも、アノーテーションのデフォルト値を変更せず、なるべく簡潔で読みやすいソースコードとすることをお薦めします。


[1] <ejb-ref-name>に設定する値は単にそのコンポーネントの環境コンテキスト名(java:comp/env以下のJNDI論理名)ですのでどんな名前をつけても構いません。実際の動作に重要なのはEJBリモート呼び出しの場合はデフォルトで設定されるJNDI物理名の方です。

[2] 対象のEJBがEJB 3.0仕様のものである場合、<ejb-ref-type>要素は省略することができます。また、<remote>要素または<local>要素にはビジネス・インタフェースのクラス名を指定しますが、<injection-target>要素で指定されているフィールドまたはsetterメソッドの引数のクラスが1つのビジネス・インタフェースを特定できるのであれば省略することができます。<injection-target>要素がない場合(インジェクションではなくContext#lookup()で取得することを前提としている場合)は<remote>要素または<local>要素を省略することはできません。詳細は、JSR 220: EJB V3.0 EJB Core Contracts and Requirements, 16.5.1.3 Declaration of EJB References in Deployment Descriptor, pp.416-417に記述されていますが、ここの部分はややこしいので、上で示したように<ejb-ref>に必要な全ての要素を省略せずに記述することをお薦めします。

[3] 逆にアノーテーションの全てのメンバを明示した方がよいと考える方もいます。おそらく、これはアノーテーション・メンバのデフォルト値が将来の仕様で変更になった場合、AppServerをバージョンアップすると意図しない動作をする可能性があることを恐れてのことだと思います。しかしながら、Java EEの将来の仕様で上位互換を捨ててまでメンバのデフォルト値が変更される可能性はとても低いのではないかと思われます。また、アノーテーション・メンバの明示が多ければ、ソースコード中に占めるアノーテーションの割合が多くなり、可読性を悪くする弊害が生じることの方が問題なのではないでしょうか。

Posted at 09:21午後 7 25, 2006 by Takashi Nishigaya in Java  |  投稿されたコメント[1]

投稿されたコメント:

cool

Posted by wow gold on 11月月 03日, 2008年 at 10:38 午前 JST #

コメント
  • HTML文法 不許可
« Java EE 5:デプロイメント記述子... | メイン | Java EE 5:Dependency... »