木曜日 7 27, 2006
木曜日 7 27, 2006
前回のエントリでは、DI(Dependency Injection)のメリットと、Java EE 5におけるDIの設定としてアノーテーションとXML形式の使い分けについて議論しました。インジェクションは非常に便利なのですが、2つの制約があることに気をつけなければなりません。1つは既に何度も述べているように、インジェクションはEJBコンテナ、Webコンテナ、クライアント・コンテナのいずれかの上でしか機能しないことです。もう1つの注意点は、インジェクションは任意のクラスで可能なわけではないということです。これは、インジェクションされる側のクラスの種類が限定されるだけでなく、そのクラスから参照されるクラスの種類も限定されます。
また、インジェクションはクラスのメンバ変数に直接値を設定するフィールド・インジェクションか、プロパティを表すsetterメソッドを経由してオブジェクトを設定するセッタ・インジェクションのどちらかに限定され、コンストラクタ・インジェクションはサポートされません。
従って、任意のオブジェクトに対してDI可能なSpring FrameworkやSeasar2に較べて、Java EE 5のインジェクションにはかなり制約があることを知っておかなければなりません。しかし、DIやAOPは無闇に使用するとかえってシステム全体の振舞を分かりにくくしてしまう恐れがあるため、DIの使用はほどほどにしておいた方がいいでしょう。その意味では、ある程度フレームワーク側で制約がある方が歯止めが効くという考え方もできなくはありません。一般的には、アプリケーションのティアをまたがって連係するコンポーネント同士が疎結合になっていること、ロジックとコンフィギュレーションが疎結合になっていれば十分であると言えます。
インジェクションが可能な(@Resourceなどのアノーテーションが宣言できる)クラスの一覧は、Java EE 5 Specification (JSR-244), EE.5.2.3節, 表EE.5-1, pp.64に明示されています。以下に抜粋します。
| 分類 | インジェクション可能なクラス |
| Web | サーブレット(javax.servlet.Servlet実装クラス), サーブレット・フィルタ(javax.servlet.Filter実装クラス), リスナ(javax.servlet.*Lstener, javax.servlet.http.*Listener,javax.el.ELContextListener実装クラス), タグハンドラ(javax.servlet.jsp.tagext.Tag実装クラス), JSFマネージド・ビーン |
| EJB | セッション・ビーン, メッセージ・ドリブン・ビーン, インターセプター |
| Webサービス | Webサービス・エンドポイント, ハンドラ(javax.xml.ws.handler.Handler実装クラス) |
| クライアント | メイン・クラス |
一方、インジェクションにより参照可能なオブジェクトにも制約があります。参照可能なオブジェクトの種類に関しては、Java EE 5 Specification (JSR-244), EE.5.4-EE.5.13, pp.68-112とJSR-220: EJB V3.0 EJB Core Contracts and Requirements, 16.4-16.15, pp.407-451に記述があります。以下に、参照可能なオブジェクト毎にインジェクションに使用するアノーテーション、対応するデプロイメント記述子の要素名、使用可能なコンテナを一覧にまとめます。
| Java type | Annotation | DD element | Container |
| 基本データ型(String, Character, Integer, Boolean, Double, Byte, Short, Long, Float) | @Resource | <env-entry> | all |
| javax.sql.DataSource, javax.jms.ConnectionFactory, javax.jms.QueueConnectionFactory, javax.jms.TopicConnectionFactory javax.mail.Session, java.net.URL, javax.resource.cci.ConnectionFactory |
@Resource | <resource-ref> | all |
| javax.resource.cci.InteractionSpec | @Resource | <resource-env-ref> | all |
| javax.jms.Queue, javax.jms.Topic |
@Resource | <message-destination-ref> | all |
| javax.transaction.UserTransaction | @Resource | <resource-env-ref> | all |
| javax.transaction. TransactionSynchronizationRegistry | @Resource | <resource-env-ref> | all |
| org.omg.CORBA.ORB | @Resource | <resource-ref> | all |
| javax.persistence.EntityManagerFactory | @PersistenceUnit | <persistence-unit-ref> | all |
| javax.persistence.EntityManager | @PersistenceContext | <persistence-context-ref> | all |
| EJB参照 | @EJB | <ejb-ref>, <ejb-local-ref> | all |
| Webサービス/ポート参照 | @WebServiceRef | <service-ref> | all |
| javax.ejb.TimerService | @Resource | <resource-env-ref> | EJB container only |
| javax.ejb.EJBContext | @Resource | <resource-env-ref> | EJB container only |
TimerServiceとEJBContextはEJBコンテナ内でのみ利用可能ですが、それ以外のオブジェクトは、クライアントコンテナ、Webコンテナ、EJBコンテナのいずれの中でも取得することができます。
それでは、上記の仕様を眺めながら、ティア間の疎結合が可能か?ロジックとコンフィギュレーションの分離が可能か?という観点で考察してみましょう。
ティア間の疎結合に関しては、プレゼンテーション(Web)層とビジネス層、ビジネス層とインテグレーション(persistence)層について考えます。Web層とビジネス層については、JSFマネージド・ビーンがインジェクション可能クラスになっているため、各ページのバッキング・ビーンにビジネスロジックとしてのセッション・ビーンを@EJBでワイヤリングすることで目的を達成することができます。ただし、これはWeb層のフレームワークにJSFを採用した場合に限定されてしまいます。JSF以外のWebフレームワークを使用する場合には@EJBは使用できません。それぞれのWebフレームワークが@EJBと同等のEJB3セッション・ビーンのインジェクションをサポートしている可能性もありますので、使用しているフレームワークの最新状況を確認してみてください。
ビジネス層とインテグレーション層の分離に関しては、Java EE 5インジェクション仕様では実現できないようです。以下のように、@ResourceアノーテーションでDAOオブジェクトをセッション・ビーンにインジェクトできればよいのですが、@ResourceはJava EE管理オブジェクト以外はStringなどの基本データ型のみがインジェクションの対象になっています。
@Stateless
public CutomerManagerImpl implements CustomerManager {
@Resource CustomerDAO dao; // NG: 基本データ型でない
:
}
もし、@Resourceが任意のビーン・オブジェクトをサポートしてくれれば上記の定義は可能だったのですが、この点は非常に残念に思います。将来のJava EE仕様でこの点が拡張されることを望みます。
しかし、そもそも、DAOの目的はデータアクセスの実装を隠蔽することでした。RDBに対するデータアクセスはJPA(Java Persistence API)で標準化されました。JPAのEntityManagerはDAOの標準APIと捉えることもできます。将来に渡ってデータ保管方法をRDBから変更する予定がないのであればDAOを作らず、直接EntityManagerを使用すると判断してもいいでしょう。
@Stateless
public CutomerManagerImpl implements CustomerManager {
@PersistenceContext EntityManager em; // 汎用(標準)DAOと捉える
:
}
ただし、上記のデザインにはいろいろと問題もありそうです(ビジネスロジックからEntityManagerを直接扱う場合の問題については別途議論したいと思います)。もし、EntityManagerを隠蔽化したいと考えるなら、そのクラスをシングルトンなDAOクラスとして実装するのではなく、CRUDのみのfine-grainedなセッション・ビーンとして定義し、course-grainedなセッション・ビーンから@EJBで参照されるようにデザインしてもいいでしょう。
次にロジックとコンフィギュレーションの分離についてはどうでしょうか。コンフィギュレーション情報のインジェクションとして使えそうなアノーテーションとしては@Resourceがあります。しかし、コンフィギュレーション情報が非常に少なければ問題ありませんが、非常に多くの構造的な情報を与えるには@Resourceは役不足です。この場合には、アプリケーション独自のコンフィギュレーションをXMLで定義し、このXMLファイルののパスのみをデプロイメント記述子の<env-entry-value>に与えるか、コンフィギュレーションのオブジェクト・ツリーをJMXのMBeanとして実装することも考えられます。
いずれの場合もSpringやSeasarと併用することも検討の候補として挙げられますが、アプリケーションが依存するフレームワークの種類は少ない方がいいでしょう。複数フレームワークの採用に関しては開発効率と同時に将来の保守性についても考慮して検討して下さい。
cool
Posted by wow gold on 11月月 03日, 2008年 at 10:40 午前 JST #