4.7 Application contexts and Resource paths

4.7.1 Constructing application contexts

ApplicationContextのコンストラクタは、普通はXMLファイルの在処を示すString(の配列)を引数としてもらう。指定したパスにprefixが指定されない場合は、ApplicationContextのクラスごとにどのResourceの実装クラスを使うかがきまる。


例えば


ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
の場合はClassPathResource となる。


また


ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");
の場合は、カレントディレクトリからの相対パスファイルシステムからリソースが取得される。(たぶんFileSystemResource)


もちろんprefixをつけて


ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
とすれば、XMLはクラスパスから取得される。

4.7.1.1 Constructing ClassPathXmlApplicationContext instances - shortcuts

ClassPathXmlApplicationContextには、使いやすいようにいくつものコンストラクタが定義されている。同一のパスにある複数のXMLを指定するための便利な方法として、まぁ以下を見れ。


com/
foo/
services.xml
daos.xml
MessengerService.class

とある場合に、


ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "daos.xml"}, MessengerService.class);

とするわけだ。こうするとMessengerServiceが格納されているパッケージ直下にある指定したXMLを全てロードすることができる。

4.7.2 Wildcards in application context constructor resource paths*1

ApplicationContextに指定するXMLファイルのパスについて、「classpath*:」というprefixを用いたり、パス自体にANTライクな正規表現を使うこと事で複数のリソースをまとめて指定できる。こう行った表記は、例えばコンポーネント指向のアプローチを取る場合に役に立つ。コンポーネントごとに決まった場所にContextの定義を定義する。最終的にアプリケーション全体としてのApplicationContextの内容は「classpath*:」prefixを付与したパス指定にもとづき全検索され、ひとつのコンテキストとして纏め上げられる。

なおワイルドカードの機能はApplicationContextのコンストラクタでのみサポートしており、Resourceの機能としてはサポートしていない。なので、Resourceのprefixにclasspath*:を指定することはできない。

4.7.2.1 Ant-style Patterns

具体例です。


/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml

Implications on portability
ワイルドカード指定したパスがファイルURLの場合は、ワイルドカードの解決はプラットフォームに依存することがない。しかしクラスパスロケーションを指定した場合はそうではない。この場合Resolverは、ワイルドカードが記述される前のリソースを(例:/classpath:com/*の場合は/comをだろう)Classloader.getResource()を呼び出すことでURLを取得する。このようなやり方で取得していった場合に複数のリソースが取得されるわけだが、この返却順は保証されていない。

In practice, it is always a java.io.File representing the directory, where the classpath resource resolves to a filesystem location, or a jar URL of some sort, where the classpath resource resolves to a jar location. Still, there is a portability concern on this operation.*2

またワイルドカードが記述される前のリソースとしてJARのURLが取得された場合は、JARのURLを解決しJARの中身の走査を行い、wildcardの解決を行う。この処理は大抵の環境では上手くいくが、たまに失敗することもあるので、JARを経由したリソースの取得を行う場合は、自環境においてしっかりテストするべきでる。

4.7.2.2 The classpath*: prefix

具体例です。


ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");

この表記は、上記の名前に該当するすべてのリソースを全てのクラスパスから取得((ClassLoader.getResources(...)))し、その内容をマージすることを指示している。

Classpath*: portability

このワイルドカードは、クラスローダのgetResourcesに依存する。クラスローダの実装はアプリケーションサーバ毎に異なるので、特にJARファイルの扱いについて挙動に差異があることがる。この機能がちゃんとうこくかを確認するためには、JARファイルの中にあるリソースをgetClass().getClassLoader().getResources("")で取得できるか試してみることだろう。このテストを、同じ名前を持つが異なるJARファイルに配置してみよう。試してみてもし不適切な動作をする場合は、アプリケーションサーバのドキュメントを参照し、クラスローダの設定についてみてみることだ。

classpath*:」 prefixはANTスタイルのワイルドカードと混ぜることができる。例えば、


classpath*:META-INF/*-beans.xml
などです。この場合はワイルドカードが定義される前までのリソースをClassLoader.getResources() で読み、ここで取得したリソースに対してPathMatcher (Antのワイルドカード形式を解決する人)に渡して処理していく。

4.7.2.3 Other notes relating to wildcards

classpath*:」prefixにANTスタイルのワイルドカードを混ぜる場合は、少なくともANTスタイルのワイルドカードを指定する前に最低ひとつは親ディレクトリが定義されていなければならない。これは例えば「classpath*:*.xml」はダメだということです。これはClassLoader.getResources()を利用することによる制約であり、このメソッドに空文字を指定するとファイルシステム上のロケーションを返すからである。*3classpath*:」prefixにANTスタイルのワイルドカードを混ぜた場合は、もし検索する親パッケージが複数のクラスパス上に定義されている場合は保証されない。
この理由は、


com/mycompany/package1/service-context.xml
というリソースは一箇所にしかないが、

classpath:com/mycompany/**/service-context.xml
というリソースはこれを解決する段階で、getResource("com/mycompany")を取得する。もしこの親パッケージが複数のクラスローダに存在する場合は、実際に欲しい方のリソースが下位のクラスローダにあるかもしれない。そういうわけで、なるべくなら「classpath*:」prefixにANTスタイルのワイルドカードを混ぜるのであれば、親パッケージを含むクラスパスの場所を全て検索できるようなパターンを定義すべきである。

4.7.3 FileSystemResource caveats

FileSystemResourceは、絶対パス相対パスの両方を扱う。相対パスは、現在の作業ディレクトリからの相対パスであり、絶対パスファイルシステムのルートからのパスである。しかしながら下位互換性の観点から、FileSystemApplicationContextをResourceLoaderとして用いる場合は話が変わってくる。FileSystemApplicationContextは、指定する文字列がスラッシュから始まろうが始まるまいが、常に相対パスとして解決する。つまり以下の二つは同じ定義である。


ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");

ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");


FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");

FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");

なので、ファイルの絶対パスを指定したい場合は、UrlResourceを使用しprefixにfile:を指定するべきである。つまりこう言うこと。


// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:/some/resource/path/myTemplate.txt");


// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:/conf/context.xml");

*1:日本語不自由なのであとでみなおすこと!

*2:ようわからんのでバス

*3:??良く解らんが・・・