EJB 3.0(Public Draft)入門記 Simplified API Chapter 8

お盆休みを理由に入門記サボっていました。いけない、いけない。

サボり癖がついてしまいましたが、なんとか復活。Chapter 8 Enterprise Bean Context and Environment に進みます。


エンタープライズBeanのコンテキストは次のものを含んでいるそうです。

  • コンテナのコンテキスト
  • リソース
  • 環境のコンテキスト

コンテキスト内のリソースや環境エントリへの参照はコンテナが提供するとあります。そして、インスタンス変数やセッターメソッドがDependency Injectionのターゲットとしてアノテートされるそうです。
アノテーションを使ったDependency Injectionの代わりに、javax.ejb.EJBContextインタフェースに追加されたlookupメソッドを使うこともできるそうです。
どちらの方法をとるにしてもコンテキストの依存を表すためにメタデータアノテーションが使われる、とあります。

環境エントリ(environment entries)ってJNDI ENCでアクセスできるEJBのことを指していると思ったのですが、デプロイメント記述のであらわされる値のことかも。

8.1 Annotation of Context Dependencies

Beanはリソースや環境コンテキスト内に登録されている他のオブジェクトへの依存をDependency Annotationを使って宣言します。
Dependency AnnotationはBeanが依存するオブジェクトやリソースの型、特徴、名前を指定するそうです。

いままで意識したことなかったですが「Dependency Annotation」という用語を使うのですね。ふーん。

例が載っています。

@EJB(name="mySessionBean", beanInterface="MySessionIF.class")
@Resource(name="myDB", type="javax.sql.DataSource.class")

アノテーションには@EJBと@Resourceって2種類あるんですね。別に一種類だけ@Injectアノテーションがあれば事足りるように思ったんですがだめなんでしょうか。ちなみに@InjectはEarly DraftまではEJB 3.0の仕様にありましたが、Public Draftではなくなっています。

Dependency AnnotationはBeanクラスかインスタンス変数かメソッドに対して使えるそうです。アノテーションをBeanクラスに指定できるというのはちょっと意外でした。

Dependency Annotationで指定する必要がある情報の量は使用しているコンテキストとコンテキストからどれくらい情報を推測できるかにかかっているようです。???

8.1.1 Annotation of Instance Variables

インスタンス変数をアノテートしてリソースや他のオブジェクトへの依存を示すことができます。コンテナがアノテートされたインスタンス変数を自動的に初期化します。この初期化はBeanのEJBContextがセットされた後かつビジネスメソッドが呼び出される前に行われるようです。

例が載っています。必要そうなとこだけ抜粋します。

@Resource(name = "myDB") // type is inferred from variable
public DataSource customerDB;

@EJB // reference name and type is inferred from variable
public AddressHome addressHome;

ルールが2つ載っています。

  • リソースの型が変数の型で決められる場合、オブジェクトの型はアノテーションに含まれる必要がない。
  • Bean環境内のリソースの参照に対する名前が変数名と同じである場合、名前はアノテーションで示される必要がない。

8.1.2 Setter Injection

セッターインジェクションはインスタンス変数インジェクションのalternativeだそうです。
EJB 3.0 ではインスタンス変数インジェクションがデフォルト扱い?
セッターインジェクションを使う場合はセッターメソッドにアノテートします、とあります。

例をコピペします。なぜか@EJBを使った例がないです。

@Resource(name=”customerDB”)
public void setDataSource(DataSource myDB) {
  this.ds = myDB;
}

@Resource // reference name is inferred from the property name
public void setCustomerDB(DataSource myDB) {
  this.customerDB = myDB;
}

@Resource
public void setSessionContext(SessionContext ctx) {
  this.ctx = ctx;
}

ルールが2つ載っています。

  • リソースの型がパラメータの型で決められる場合、オブジェクトの型はアノテーションに含まれる必要がない。
  • リソースの名前がセッターメソッドに対応するプロパティの名前と同じ場合、名前は明示的にアノテーションで示される必要がない。

型はパラメータを見て、名前はプロパティを見るということらしいですが、型も名前もプロパティを見るというように統一したほうがすっきりするような気がするんですが駄目なんでしょうか?ちょっと疑問です。

Dependency AnnotationをつけられたセッターメソッドはBeanのEJBContextがセットされた後かつビジネスメソッドが呼び出される前に行われる、とあります。

8.1.3 Injectioin and Lookup

リソース、コンポーネントへの参照、JNDIからlookupできる他のオブジェクトのインジェクトは上述したインジェクションメカニズムにより行われるそうです。
インジェクトされるオブジェクトへの参照のlookupは名前(インスタンス変数インジェクションではインスタンス変数の名前、セッターインジェクションではプロパティの名前)と型(インスタンス変数インジェクションではインスタンス変数の型、セッターインジェクションではパラメータの型)によって行われるということです。
これらのlookupはBeanのjava:/comp/envネームスペース内で行われるそうです。

そういえば、EJB 3.0の場合、DIコンテナにあるコンストラクタインジェクションがないですね。EJBだし仕方ないのか。

8.1.4 EJB Context

javax.ejb.EJBContext インタフェースに次のメソッドが追加されたそうです。

Object lookup(String name)

このメソッドはJNDIからリソースやオブジェクトをlookupするために使われます。


それでは、実際にDIが行われるコードを動かしてみます。やってみたことはこんなこと。

  • Dependency Annotationですべての情報を指定しなくてもコンテナがコンテキストから情報を推測してDIしてくれるみたいなので、このあたりを試してみる。
  • インスタンス変数インジェクションをつかってみる。(セッターインジェクションよりもこっちがdefaultっぽいので)
  • javax.ejb.EJBContextを拡張しているSessionContextのlookupメソッドを使ってみる。

ビジネスインタフェースEcho

public interface Echo {
  String echo(String arg);
}

Echoを実装したステートレスセッションBean:ローカルステートレスセッションBean。@Statelessにnameを指定している。

@Stateless(name="echo")
public class EchoBean implements Echo {
  public String echo(String arg) {
    return arg;
  }
}

ビジネスインタフェースHoge

public interface Hoge {
  void hoge();
}

Hogeを実装したリモートセッションBean:Echoにアクセス

@Stateless
@Remote(Hoge.class)
public class HogeBean implements Hoge {

  @EJB // 型と変数名でinject
  private Echo echo;
  
  @EJB // 型でinject
  private Echo echo2;
  
  @EJB(businessInterface=Echo.class) // 指定された型でinject
  private Object echo3;

  @EJB(beanName="echo") // 指定された名前でinject
  private Object echo4;
  
  @Resource
  private SessionContext ctx;
    
  public void hoge() {
    System.out.println(echo.echo("1"));
    System.out.println(echo2.echo("2"));
    System.out.println(((Echo)echo3).echo("3"));
    System.out.println(((Echo)echo4).echo("4"));
    
    Echo echo5 = (Echo) ctx.lookup(Echo.class.getName());
    System.out.println(echo5.echo("5"));
  }
}

クライアント

public class Client {
  public static void main(String[] args) throws Exception {
    InitialContext ctx = new InitialContext();
    Hoge hoge = (Hoge) ctx.lookup(Hoge.class.getName());
    hoge.hoge();
  }
}

実行結果:デプロイしてクライアントを実行するとこんなカンジに出力されます。

14:51:35,729 INFO  [STDOUT] 1
14:51:35,739 INFO  [STDOUT] 2
14:51:35,739 INFO  [STDOUT] 3
14:51:35,739 INFO  [STDOUT] 4
14:51:35,739 INFO  [STDOUT] 5

出力結果をみてみると、DIやlookupがうまくいっていることがわかります。2番目のパターン(インスタンス変数の型でinject)でもOKだとは思っていなかったです。この場合、自動バインディングされていると言っても大丈夫?でも、「コンテキストから情報を推測」とか言っているけど、どのように情報を取得するのかって明文化されているんでしょうか。そうじゃないとコンテナ依存になってしまうような気がします。


この章ではとにかく、「EJB Core Contracts and Requirements」のchpter 15 を参照しろと何度も言ってましたが、あまりしっかり参照してませんです。DIするにあたってどのようにコンテキストから情報を推測するのか気になるし、ちゃんと読んでみないといけないなぁ。

あと@EJBや@Resourceをクラスに指定できるというので、試してみたのですが、うまくいきませんでした。この章では、クラスに指定できるとは書いてあっても例がないんですよね...。


とりあえず chapter 8 終了。4ページでした。