
Page | 1 | 2 |
DI コンテナの本当の使いどころ
|
DIコンテナは、現在最も注目度の高い技術の1つです。今年中に正式リリース予定のJ2EE 5.0では、EJB 3.0 にDI 技術が取り入れられます。Web アプリケーションの開発や保守の効率を高めてくれる技術に間違いありませんが、使い方を誤ればかえって現場を混乱に陥れます。今回は、DI コンテナを正しく認識し、最大の効果を得るための使いどころと、よく見られる誤った使用法(アンチパターン)について解説します。
DI の自由度は諸刃の剣
近ごろ、「実プロジェクトでDIコンテナ注1を導入している」という話をちらほら耳にするようになりました。それと同時に、「DIコンテナを使ったプロジェクトが大変なことになっている」という話も耳にするようになりました。DIの魅力を十分に享受して低コスト、高品質を実現しているプロジェクトがある一方で「DIを導入してみたのはいいのだけれど、DIの設定ファイルが大きくなりすぎて管理しきれない」「DIを使っているのに、テスタビリティが全然向上していない」など苦労しているプロジェクトもあるようです。この差はいったいどこから来るのでしょうか。DIは、EJBなどと比べると比較的取っ付きやすい技術ではありますが、ほかの技術同様、誤った使い方では十分に力を発揮できません。DIコンテナは非常に単純明快な技術ではありますが、そのシンプルさ故に自由度が高くさまざまな使い方ができます。そのため、無作為に利用してしまうとかえってデメリットばかりになってしまいます。自由度が高いことの代償として、利用するためにはある程度のノウハウが必要となります。このノウハウをきちんと生かしているかどうかが、DIを上手に利用できているプロジェクトとできていないプロジェクトの差となっているようです。そこで今回は、DIを安全に使うために「DIの使いどころ」と「DIのアンチパターン」を説明したいと思います。なお、サンプルコードはSpringFrameworkを前提に記述しています。
DI の使いどころ
まずは、DIの典型的な利用パターンを3つ紹介しましょう。この3つ以外にもDIを活用できる場所はありますが、DIを初めて導入する場合は、まずこの辺りを足がかりにすると良いと思います。
ServiceLocator/Factory パターンの代わり
最もオーソドックスなDIコンテナの利用方法は、ServiceLocatorパターン注2の代わりです。要するに、「レイヤ間の結合にDIコンテナを利用する」というものです。通常、J2EEのWebアプリケーションはプレゼンテーションレイヤ、ビジネスロジックレイヤ、インテグレーションレイヤ注3の3層構造で構築されます。レイヤ間はメンテナンス性や再利用性を考慮して疎結合にするのが一般的ですので、ここがDIコンテナの使いどころとなります(図1)。

図1 DIコンテナでレイヤ間を接続
ステートレスセッションBeanやO/Rマッピングフレームワークなどを利用したJ2EE Webアプリケーションでは、プレゼンテーションレイヤとビジネスロジックレイヤの間をServiceLocatorパターンもしくはJNDI(JavaNaming and Directory Interface)で、ビジネスロジックレイヤとインテグレーションレイヤの間をFactoryパターンもしくはローカル呼び出しでつなぐのが一般的です(図2)。

図2 従来のJ2EE Web アプリケーションのレイヤ接続方法
しかしこの方法では、2つのレイヤ間の接続方法が異なるために管理が面倒であったり、設計次第ではビジネスロジックレイヤとインテグレーションレイヤ間が密結合になってしまうといった問題がありました。ここで、レイヤ間の接続にDIコンテナを利用すると、プレゼンテーションレイヤとビジネスロジックレイヤの間もビジネスロジックレイヤとインテグレーションレイヤの間も同じDIという機構を用いて接続することができ、管理が楽になります。また、レイヤ間接続の共通基盤としてDIコンテナを採用することで、各レイヤの呼び出し方法が共通化されるため、AOP注4の利用も実現しやすくなります。 また、DIコンテナはServiceLocatorパターンのほかにFactoryパターンを実装する代わりとしても利用できます。DIコンテナはオブジェクトの管理機能を持っており、クラス名からオブジェクトを生成したり、生成されたオブジェクトを管理したりすることができます。この機能を利用して、DIコンテナを汎用的なFactoryクラスとして利用することができます。Factoryパターンを実装する場合、処理の一部をDIコンテナに委譲することで、高機能なFactoryパターンを非常に簡単な実装で実現できます。
Singleton パターンの代わり
DIコンテナは、Singletonパターン注5を実装する代わりにもなります。DIコンテナは、DIを実現するためにオブジェクトの管理機能を持っています。このオブジェクトの管理機能を利用することで、オブジェクトの数をSingletonパターンのように1つだけに制限することができます。クラスをSingletonパターンで実装しなくても、実行時にDIコンテナを利用することで同様に扱うことが可能になります(図3)。

図3 DI コンテナによるSingleton の実現
Singleton パターンは容易にオブジェクトを使いまわすことができるために気軽に利用されがちですが、実はさまざまな問題を引き起こします。JavaでSingletonパターンを実装する場合、通常唯一のオブジェクトをクラス変数(static変数)として保持します。クラス変数はClassLoader注6オブジェクトごとに確保されるため、複数のWAR注7を含むEAR注8を作成したりしてClassLoaderオブジェクトの階層構造が複雑になっている場合や、独自開発のClassLoaderクラスを使用する場合などでは、開発者の思わぬ誤解からバグの原因となることがあります。例えば、あるWAR 内のコードで知らず知らずのうちに別のWAR 内のクラス変数にアクセスしてデータを壊してしまう、といったことが起きたりします。また、Singletonパターンではコンストラクタをprivateにすることが多いため、実装したクラスを外部で再利用しにくい、ユニットテスト(単体テスト)を行ないづらいといった問題もあります。DIコンテナによるSingletonパターンの代行では、このような問題をなくして使い勝手が良くなっています。そのため、DIコンテナを使用している環境では、クラスをSingletonパターンで実装する前に、DIコンテナに任せられないかを一度考えてみると良いでしょう。ただし、DIコンテナ上で利用するクラスはコンストラクタをpublicにする必要があります。そのため、ほかの開発者が誤ってオブジェクトを1つ以上生成するようなコーディングをしないように、何らかの対策を施す必要があります。
Properties クラスの代わり
ある程度の規模のアプリケーションを開発する場合に必要になるのが、アプリケーション固有の設定情報の管理機能です。デバッグモード用フラグ、トランザクションタイムアウト時間、対外接続用コネクタの接続先など、多種多様な設定情報がアプリケーション固有の設定情報として必要になります。これら設定情報を管理するために多くのプロジェクトで利用されているのがPropertiesクラスです。PropertiesはJ2SE( Java2 StandardEdition)に標準で用意されているクラスで、キーと値のペアを簡単に管理できます。さらにそのデータをファイル(ストリーム)から読み込んだり、書き出したりする機能もサポートしています。そのため、ファイルでアプリケーション固有の設定情報を管理するのにうってつけです。 しかし、Propertiesクラスにも弱点があります。全角文字の扱いにクセがあることと、必ずキーと値のペアでデータを保持しなければならないことです。Propertiesクラスを利用してファイルからデータを読み込むには、native2asciiというツールを利用して、ファイル内にある全角文字をすべてUnicodeエスケープ注9しなければなりません注10(図4)。

図4 Unicode エスケープ
これが意外とくせもので、デバッグや障害対応の際などに「アプリケーション内の設定ファイルをそのまま読めない」といった困った状況を生みます。また、Propertiesクラスはキーと値のペアのみでデータを管理するため、XML のようにデータを階層構造にすることはできません。そのため、多少でも複雑な構造を持つデータを保持するためには、アプリケーション側での対応が必要となります。Propertiesクラスの代わりにXMLを使用すればこれらの問題を解決できますが、DOMやSAXによるXML処理をコーディングするのは何かと面倒です。そこで登場するのがDIコンテナです。ほとんどのDIコンテナはXMLデータとオブジェクトをマッピングする機能を持っているので、非常に簡単にXMLデータをオブジェクト化してくれます。多くのDIコンテナではオブジェクトのフィールド型に合わせてデータ形式を変換してくれますので、boolean値やint値などの取り扱いも簡単です。オブジェクトの初期化メソッドを指定できるDIコンテナを利用すれば、設定内容のチェックも簡単に行なえます。 また、前述のとおり、DIコンテナではSingletonパターンのようにオブジェクトを使いまわすことも可能ですので、設定情報を管理するクラスをSingletonパターンで設計せずに済みます。さらに、DIコンテナを使うことにはうれしい副作用があります。DIコンテナは、設定ファイルに記述された文字列のクラス名からオブジェクトを生成する機能を持っています。そのため、リフレクション注11などを利用することなく、プラグインのような機構を簡単に実現することができます。アプリケーションの中でもメンテナンスされやすい場所や再利用したい場所をこの機能を使って差し替え可能にしておくと、設定ファイルの書き換えのみで機能を変更できるようになり、メンテナンスなどがぐっと楽になります。
(注1)DI(Dependency Injection)は「依存性注入」などと訳され、主にクラス間の結合を緩く(疎結合に)するための技術。DI コンテナはその実行環境。
(注2)サービス利用者からの要求に従って、サービスを提供するオブジェクトを返す責務を持つクラスを表わすデザインパターン。
(注3)RDBMS などの外部アプリケーションとの連携を行なうレイヤ。
(注4)アスペクト指向プログラミング(Aspect OrientedProgramming)。複数のプログラムで実行する共通の処理を別途記述しておき、それを呼び出すタイミング/条件を設定して利用する。
(注5)クラスごとに1つのオブジェクトしか生成されないデザインパターン。複数スレッドから利用される場合でも、同一のオブジェクトが利用される。
(注6)Java バイトコードからクラス情報を読み込む責務を持つJ2SE 標準のクラス(java.lang.ClassLoader)。
(注7)Web ARchive の略。Web アプリケーションを格納するために策定されたJar の拡張規格。
(注8)Enterprise ARchive の略。エンタープライズアプリケーションを格納するために策定されたJar の拡張規格。
(注9)“\u”に続けてUnicode のコードを付記する文字の表記方法。例えば、“あ”をUnicode エスケープすると“\u3042”となる。
(注10)native2asciiを実行しながら読み込むストリームを用意する手もありますが……。
(注11)クラスやメソッド、フィールドといった情報を扱い、オブジェクトの生成などの処理を動的に行なうためのAPI。例えば、“jp.co.ulsystems.Sample”といった文字列からSample クラスのオブジェクトを生成することができる。
Page | 1 | 2 |













