Edited at

Mavenのresourcesフォルダについて整理してみた

More than 1 year has passed since last update.


動機

今までなんとなくMavenでresourcesフォルダを使ってきましたが、開発時にEclipseから使う場合にどこのファイルが使われるかをちゃんと理解していなかったので、まとめてみます。


そもそもリソースとは

Javaにおけるリソースとは、プログラム実行する際に必要となる、プログラム本体(classファイル)以外のファイルのことです。よくあるものとしては、設定ファイルや画像ファイル、各種データファイルなどになります。クラスパス上にファイルを配置すれば、getClass().getResourceAsStream("/<filename>")でInputStreamを取得できます。

Mavenを使っている場合は、src/main/resourcesとsrc/test/resourcesの2つのリソース用の専用ディレクトリがあるので、そこにリソースファイルは配置します。で、この2つの違いなんですが、mainの方は通常実行時に、testの方はJUnitのテスト実行時に使われる、というざっくりとして説明しかされないことが多いのですが、もう少し詳しく見ていきます。


Eclipse

EclipseのMavenプロジェクトでは、上記の2つのディレクトリは両方ともソースディレクトリして登録されています。そしてOutputフォルダがtarget/classes、target/test-classesになっており、クラスファイルの出力先と同じになっています。これらのフォルダには実行時にクラスパスが設定されますので、これらのフォルダ内にあるリソースファイルが使われることになります。

ということなので、まず第一のポイントとして、srcフォルダ配下にあるファイルが直接使われることはありません。それではいつtargetフォルダの方にコピーされるのでしょうか?ビルド時でしょうか?そうではなく、Eclipseが更新を認識した際になります。Eclipse上から直接ファイルを作成したり、更新した場合はそのタイミングで、Eclipseの外でファイル操作をした場合は、Eclipse上でsrcフォルダを(F5やメニューから)refreshしたタイミングで、targetの方にコピーが行われます。これを私はよく理解していなくて、最初のうちはtargetフォルダの方を直接編集して、あとからsrcの方に手でコピーするというわけのわからないことをしていました。。。

第二のポイントしては、classes(mainからコピー)とtest-classes(testからコピー)のどちらのファイルが使われるかです。通常実行時は、クラスパスはclassesの方にしか設定されませんので、classes内のファイルが確実に使われます。それに対して、JUnit実行時は両方のフォルダにクラスパスが設定されます。となると、どちらが使われるかは、単純にどちらが先にクラスパスに設定されているか、ということになります。実際にテストクラスからSystem.getProperty("java.class.path")で確認してみると、test-classesが1番目、classesが2番目に設定されていることがわかります。なので、test-classesの方が優先で使われるというわけです。


Maven

続いてMavenから実行する場合です。標準的な構成ではMavenからプログラムを実行するのは、testフェーズでJUnitを実行する時だけなので、その時どこのファイルが使われるかの話をすると、やはりsrc/main/resourcesとsrc/test/resourcesファルダにあるリソースファイルは、process-resourcesとprocess-test-resourcesフェーズで、各々target/classesとtarget/test-classesファルダにコピーされます。そして、JUnit実行時はクラスパスが両方に通りますが、test-classesの方が先なのでそちらが優先的に使われます。Eclipseでの実行時と同じですね。

あと、Mavenがらみでいうと、最後にpackageフェーズでjarファイルなどを作成する際には、当然target/classesフォルダの方から作られます。