JBoss EJB 3.0と拡張機能

  サービスオブジェクトの注入
はじめに

ここまで、いろいろなサービスオブジェクトの作成方法と、どのようにクライアント側から使用するかを学んできました。基本的にはJNDIを利用してサービスのスタブオブジェクトをルックアップします。しかし、サービスオブジェクトが増えてくると、すぐにルックアップのコードは長く乱雑になってしまいます。また、サービスをルックアップするには、サーバ側とクライアント側で事前に各サービスの名前を取り決めておく必要があり、開発者がそれらを記録しておく必要があります。ルックアップ後は、サービスのスタブオブジェクトを適切なインタフェースの型に手動でキャストする必要があり、型が違っていた場合には実行時に例外が発生してしまいます。さらに、サービスのルックアップのコードは、全アプリケーションに渡って重複され散らかっています。

EJB 3.0アプリケーション内でオブジェクトを取得する場合、JNDIのルックアップよりもはるかに良い簡単な方法は、依存性注入のデザインパターンを使うことです。このトレイルでは、EJB 3.0の依存性注入について学びます。

依存性注入(Inversion of Control: IoCとも言います)の背後にある考え方は、各アプリケーションコンポーネントがどんなタイプのサービスオブジェクトを必要としているか(これを依存性と言います)を宣言するということです。コンテナがアプリケーションコンポーネント間の依存性を解決し、サービスオブジェクトをインスタンス化し、サービスのスタブを実行時にアプリケーションに注入します。注入は自動的にJavaBeanのセッターメソッドを呼び出すか、あるいは直接データフィールドへ値を代入することで行います。

EJB 3.0は、依存性注入のデザインパターンの実装をとてもエレガントな方法、つまりアノテーションで提供しています。では、どのように使用するか見ていきましょう。

EJB 3.0の依存性注入アノテーションはEJBオブジェクトでのみ使用できることに注意してください。一般的なPOJOでは利用できません。たとえば、サーブレットに依存性注入アノテーションを使用してセッションBeanオブジェクトを取得することはできません。さらに、EJB 3.0の依存性注入はローカルJNDIでのみ使用できます。つまり、リモートサーバからオブジェクトを注入することはできません。

サンプルアプリケーション

このトレイルでは、注入リソースをたくさん利用するサンプルアプリケーションを見ていきます。概観はメッセージ駆動型Beanのサンプルアプリケーションとだいたい同じです。CalculatorMDBは、メッセージキューqueue/injectionの終端ですが、サービスそのものは実行せず、計算と結果保存の仕事を2つのステートレスセッションBean、CalculatorBeanRecordManagerBean、に委譲します。これら2つのBeanのスタブオブジェクトがCalculatorMDBに注入されます。

RecordManagerBeanは、check.jspページがルックアップできるように、計算結果をサーバの組み込みデータベースに保存します。RecordManagerBeanはJNDIで注入されたJDBCリソース経由でデータベースにアクセスします。

@EJBでEJBを注入する

EJBのスタブを注入するには@EJBタグを使用します。以前のトレイルで見たJSPページのセッションBeanのルックアップのコードは不要になります。下の例では、CalculatorMDBクラスのフィールド変数rmRecordManager型で、これはRecordManagerBeanステートレスセッションBeanのローカルインタフェースです。つまり、コンテナはRecordManagerBeanのスタブを取得する方法を見つけ、最初に使用される前にrmにスタブを割り当てます。開発者の視点で見ると、ルックアップをしなくてもrmに自動的に値が代入されます。


public class CalculatorMDB implements MessageListener {

  @EJB RecordManager rm;
  
  // use the rm variable
  // ... ...
}

あるいは、依存性注入のアノテーションをセッターメソッドに使用することもできます。つまり、下に示すコードは上記のコードと全く同じ動作になります。コンテナが自動的に行ってくれるので、開発者はsetCal()メソッドを呼ぶ必要はありません。


public class CalculatorMDB implements MessageListener {

  RecordManager rm;
  
  @EJB
  public void setRm (RecordManager rm) {
    this.rm = rm;
  }
  
  // use the rm variable
  // ... ...
}

ある状況下では、コンテナがBeanインスタンスを見つける方法を指定するために、@EJBアノテーションにいくつかの属性を指定することもできます。

  • beanName属性はEJBのクラス名(パッケージ名無し)を指定します。あるいはEJBにXML記述子がある場合は、ejb-name属性を指定します。
  • beanInterface属性は、変数の型がビジネスインタフェースの親クラスだった場合に、Beanのローカルビジネスインタフェースまたはリモートビジネスインタフェースを指定します。Beanインスタンスが注入され、自動的にフィールド変数の型にキャストされます。
  • mappedName属性はBeanインスタンスのJNDI名を指定します。デフォルトのJNDI名は、"EARファイルの基本名"/"Beanクラス名"/local(またはremote)です。

下に示すコードは、beanName属性とmappedName属性を使用してStatelessCalculatorセッションBeanを見つけ、注入する例です。


public class CalculatorMDB implements MessageListener {

  @EJB (beanName="StatelessCalculator")
  // or @EJB (mappedName="EJB3Trail/StatelessCalculator/local")
  Calculator cal;
  
  // use the cal variables
  // ... ...
}

最後の注入方法は、@EJBsアノテーションを使用して、配列に複数のEJBインスタンスを注入することです。

@Resourceで任意のリソースを注入する

@EJBアノテーションはEJBスタブのみを注入します。さらに汎用的な依存性注入用のアノテーションは@Resourceです。RecordManagerBean@Resourceを使ってサーバのデフォルトのDataSourceである組み込みのHSQLデータベースを注入します。Beanは各計算結果を、DataSourceから取得したJDBCコネクションを使ってデータベースに保存します。Web層のcheck.jspページはデータベースを定期的にチェックし、新たな計算結果が取得できたら表示します。アノテーションのname属性の"java:/DefaultDS"DataSourceのグローバルJNDI名を表します。


public class RecordManagerBean implements RecordManager {

  // Inject resources based on jndi names
  @Resource (mappedName="java:/DefaultDS")
  DataSource myDb;
  
  // use myDb to obtain JDBC connection
  // and operate the database ...
}

一般的には、オブジェクトのJNDI名を使えばどのサービスオブジェクトでもJNDIから注入できます。JNDIオブジェクトがローカルなJNDIツリー(java:comp/env)にある場合は、マップされた名前を接頭辞なしに指定すれば、自動的にローカルツリーが検索されます。メッセージのコネクションファクトリとメッセージキューを注入する例を下に示します。


@Resource (mappedName="ConnectionFactory")
QueueConnectionFactory factory;

@Resource (mappedName="queue/A")
Queue queue;

「よく知られた」オブジェクトであれば、JNDI名がなくても@Resourceアノテーションで注入できます。オブジェクトの型からオブジェクト名を推測します。たとえば、次のようなものがあります。


@Resource
TimerService tms;

@Resource
SessionContext ctx;

@EJBアノテーションと同様、@Resourceアノテーションはセッターメソッドにも配列にも使用できます。

その他の依存性注入アノテーション

@EJBアノテーションも@Resourceアノテーションも、注入するリソース用に特注されています。開発者の作業を簡単にするためです。もう1つの重要な依存性注入アノテーションはEntityManagerオブジェクトの注入です。EntityManagerオブジェクトはEJB 3.0コンテナでPOJOデータの永続性処理を行います。EntityManagerとその依存性注入アノテーションについては後のトレイルで学習します。

ソースコード参照

サーバ

  • CalculatorMDB.java: queue/injectionキューのメッセージを処理するメッセージ駆動型Bean。2つのセッションBeanが注入される。
  • CalculationRecord.java: CalculatorMDBの個々の計算結果を保存するデータオブジェクトクラス。オブジェクトはサービス要求メッセージのタイムスタンプ"sent"で一意に識別される。
  • RecordManager.java: サーバ組み込みデータベースの計算結果レコードを保存したり取得したりするセッションBeanのインタフェース。
  • RecordManagerBean.java: サーバ組み込みデータベースの計算結果レコードを保存したり取得したりするセッションBeanの実装。DataSourceに対応したデータベースが注入される。

クライアント

  • calculator.jsp: ユーザからの入力を受け取り、投資計算のサービス要求メッセージを送信するクライアントJSP。
  • check.jsp: RecordManagerを使用してCalculatorBeanの処理が終了するのを待つクライアントJSP。待機するサービス要求メッセージは、メッセージのタイムスタンプ"sent"で識別される。
まとめ

このトレイルでは、アプリケーションにサービスオブジェクトを注入する依存性注入アノテーションの使い方についてお教えしました。依存性注入のデザインパターンは、コードを単純にし、アプリケーションの構造を改善します。これでEJB 3.0サービスオブジェクト管理のトレイルは終了です。

このトレイルのRecordManagerBeanセッションBeanは、注入されたDataSourceとJDBCを使いリレーショナルデータベースからデータを取得・保存しています。しかしJavaアプリケーションでデータベースを管理するさらに良い方法は、データPOJOを使うことです。次のハイキングでは、EJB 3.0のエンティティBeanについて学習し、POJOデータの永続性とデータアクセスについて学びます。