環境
Java
openjdk 11 2018-09-25
Gradle
Gradle 4.10.2
IntelliJ
IntelliJ IDEA 2018.2.4 (Community Edition)
Build #IC-182.4505.22, built on September 18, 2018
OS
Windows 10
発生する問題
プロジェクト構成
|-build.gradle
`-src/main/
|-java/
| |-module-info.java
| `-foo/
| `-Foo.java
`-resources/
`-resource.txt
コード
build.gradle
apply plugin: "application"
sourceCompatibility = 11
targetCompatibility = 11
compileJava.options.encoding = "UTF-8"
mainClassName = "foo/foo.Foo"
compileJava {
doFirst {
options.compilerArgs = [
"--module-path", classpath.asPath
]
classpath = files()
}
}
run {
doFirst {
jvmArgs = [
"--module-path", classpath.asPath,
"--module", mainClassName
]
classpath = files()
}
}
module-info.java
module foo {
exports foo;
}
Foo.java
package foo;
import java.net.URL;
public class Foo {
public static void main(String[] args) throws Exception {
URL resource = Foo.class.getResource("/resource.txt");
System.out.println("resource=" + resource);
}
}
実行結果
> gradle run
resource=null
-
/resource.txt
が取得できずにnull
になる - IntelliJ に取り込んでも同じ結果になる
- ちなみに、 Eclipse (Photon) だと問題なく取得できる
原因(と思われるもの)
- Gradle と IntelliJ では、
src/main/java
とsrc/main/resources
のビルド結果が、それぞれ別のディレクトリに出力される
gradleのビルド結果
|-build
: |-classes/java/main/
: | `-foo/
: | `-Foo.class
: |
: |-resources/main/
: : `-resource.txt
IntelliJのビルド結果
|-out/production/
: |-classes/
: | `-foo/
: | `-Foo.class
: |
: `-resources/
: `-resource.txt
- このとき、モジュールパスがどのように設定されるか確認する
Foo.java
package foo;
import java.net.URL;
import java.util.stream.Stream;
public class Foo {
public static void main(String[] args) throws Exception {
System.out.println("[jdk.module.path]");
Stream.of(System.getProperty("jdk.module.path")
.split(";"))
.map(p -> " " + p)
.forEach(System.out::println);
URL resource = Foo.class.getResource("/resource.txt");
System.out.println("resource=" + resource);
}
}
Gradleの実行結果
[jdk.module.path]
...\build\classes\java\main
...\build\resources\main
resource=null
IntelliJの実行結果
[jdk.module.path]
...\out\production\classes
...\out\production\resources
resource=null
- それぞれがモジュールパスに設定されている
- つまり、
resources
ディレクトリはfoo
モジュールとは別のモジュール扱いになっている気がする-
module-info.class
が無いディレクトリをモジュールパスに入れた場合は、無名モジュール扱いになる??? - jar ファイルなら、ファイル名からモジュール名が解決されて自動モジュール扱いになるが、ディレクトリの場合はどうなるのかよくわかってない
-
- そして、
Class.getResource(String)
の JavaDoc を確認すると、次のように説明されている
このクラスが名前付きModuleにある場合、このメソッドはモジュール内のリソースを検索しようとします。
(中略)
リソースが呼び出し元モジュールに対して開かれていないパッケージ内の非".class"リソースである場合、このメソッドはnullを返します。
getResource(String) | Class | Java 10
- つまり、
Class.getResource(String)
は、そのクラスが属するモジュール内のリソースだけを検索しようとする - もしリソースが他のモジュールだった場合、そのパッケージが開かれている(
opens
指定されている?)必要がある - もし
resources
が無名モジュールとして扱われているのであれば、名前付きモジュールから無名モジュールにはアクセスできない気がする- 少なくとも、クラスはアクセスできない
- リソースもアクセスできないかは、正確なところはわかってない
- ちなみに、 Eclipse の場合は
src/main/java
もsrc/main/resources
も、どちらもbin/main
の下に出力されている
話を整理
- Gradle, IntelliJ の場合、
src/main/resources
ディレクトリ以下のファイルは、src/main/java
以下のコンパイル結果とは違うディレクトリに出力される -
src/main/java
,src/main/resources
の出力先は、別々のディレクトリとしてモジュールパスに指定されることになる -
src/main/resources
はsrc/main/java
とは異なるモジュール扱いになる(おそらく無名モジュール?) -
Class.getResource(String)
は、基本的に同じモジュール内か、開かれているモジュール内のリソースにしかアクセスできないっぽい - したがって、
src/main/resources
以下に配置したファイルを、リソースとして読み取ることができなくなる
あくまで予想。
解決方法
Can't access resource with Java 10 – IDEs Support (IntelliJ Platform) | JetBrains
同じようなことで困っている人がいるっぽい。
ここで最終的に説明されている方法だと、 src/main/resources
の出力先を src/main/java
の出力先と同じにすればいいとのこと。
build.gradle
apply plugin: "application"
+ apply plugin: 'idea'
sourceCompatibility = 11
targetCompatibility = 11
compileJava.options.encoding = "UTF-8"
mainClassName = "foo/foo.Foo"
+ sourceSets {
+ main {
+ output.resourcesDir = java.outputDir
+ }
+ }
+
+ idea.module.outputDir file("out/production/classes")
compileJava {
doFirst {
options.compilerArgs = [
"--module-path", classpath.asPath
]
classpath = files()
}
}
run {
doFirst {
jvmArgs = [
"--module-path", classpath.asPath,
"--module", mainClassName
]
classpath = files()
}
}
- IntelliJ の出力先と、 Gradle の出力先をそれぞれ調整している
実行結果
Gradleの実行結果
> gradle run
[jdk.module.path]
...\build\classes\java\main
resource=file:/.../build/classes/java/main/resource.txt
IntelliJの実行結果
[jdk.module.path]
...\out\production\classes
resource=file:/.../out/production/classes/resource.txt
-
src/main/resources
の出力先を揃えることで、リソースにアクセスできるようになった