IDEA 2018.2.1以降、IDEは、モジュール化されていない依存関係から「モジュールグラフにない」エラー強調パッケージを開始します。module-info.Java
を追加しましたプロジェクトにファイルを追加し、必要なrequires
ステートメントを追加しましたが、src/main/resources
ディレクトリのリソースファイルにアクセスできません。
(完全な例については、 このGitHubプロジェクト を参照してください。)
./gradlew run
または./gradlew installDist
+結果のラッパースクリプトを使用すると、リソースファイルを読み取ることができましたが、IDEからアプリを実行したときはできませんでした。
JetBrainsで issue を提出したところ、IDEAはモジュールパスを使用していたが、Gradleはデフォルトでクラスパスを使用していた。 build.gradle
のブロックに続いて、Gradleをalso…にして、リソースファイルを読み取ることができませんでした。
run {
inputs.property("moduleName", moduleName)
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--module', "$moduleName/$mainClassName"
]
classpath = files()
}
}
「パッケージ」として関心のあるリソースディレクトリをexport
- ingしようとしましたが、コンパイル時に次のエラーでビルドエラーが発生しました。
エラー:パッケージが空または存在しない:mydir
opens
の代わりにexports
を使用すると、同じエラーが発生しましたが、警告に格下げされました。
mydir
リソースディレクトリをsrc/main/Java
の下に移動してみましたが、同じエラー/警告が発生し、リソースがbuild
ディレクトリにコピーされませんでした。
Java 9)のリソースはどこにあると思われ、どのようにアクセスしますか?
注:問題の調査を続けた後、この質問をかなり編集しました。最初の質問では、リソースディレクトリ内のファイルを一覧表示する方法も見つけようとしていましたが、調査の過程で、それがレッドニシンであると判断しました。リソースディレクトリの読み取りは、リソースがfile:///
のURLから読み取られている場合(それでもそうでない場合もあります)、2番目にプレーンファイルも機能していないため、問題はディレクトリではなく、リソースファイル全般にあることが明らかです。
ソリューション:
スローの答え ごとに、build.gradle
に以下を追加しました:
// at compile time, put resources in same directories as classes
sourceSets {
main.output.resourcesDir = main.Java.outputDir
}
// at compile time, include resources in module
compileJava {
inputs.property("moduleName", moduleName)
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
'--patch-module', "$moduleName="
+ files(sourceSets.main.resources.srcDirs).asPath,
'--module-version', "$moduleVersion"
]
classpath = files()
}
}
// at run time, make Gradle use the module path
run {
inputs.property("moduleName", moduleName)
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--module', "$moduleName/$mainClassName"
]
classpath = files()
}
}
サイドノート:興味深いことに、私がしない場合は、run
を作成するSlawのコードを追加し続けますファイルのリストを提供する代わりに、タスクがJARに対して実行され、実行タスクでリソースディレクトリInputStream
を読み取ろうとします IOException
をスローします(JARに対しては、単に空のInputStream
を取得します。)
ジグソーのサポートに関する Gradleの叙事詩 から私は以下で説明されているプロセスを容易にする可能性のあるプラグインについて学びました: gradle -modules-plugin 。エピックは chainsaw (experimental-jigsawプラグインの分岐)のような他のプラグインについても言及しています。 残念ながら、私はまだそれらのいずれも試していませんので、仮にそれがどれだけうまくリソースを処理しているかについてコメントすることはできません。 私はgradle-modules-pluginを試すことをお勧めします、それは少なくともジグソーモジュールを使用するのに必要な基本的な設定をかなり苦もなく扱うようです。そうは言っても、私が知る限り、Gradle 5.5.1の時点ではJigsawモジュールに対するファーストクラスのサポートはまだありません。
バウンティでは、GradleおよびJigsawモジュールでリソースを処理するための「正しい方法」に関する公式ドキュメントをリクエストします。私の知る限り、Gradlestill(4.10-rc-2以降)は「正しい方法」がないため、 tジグソーモジュールのファーストクラスのサポートがあります。あなたが得る最も近いのは Building Java 9 Modules documentです。
ただし、 メンション これは、モジュール内から(つまり、外部モジュールからではなく)リソースにアクセスすることについてです。これは、単純なbuild.gradle
構成で修正するのは難しくありません。
デフォルトでは、Gradleはクラスとリソースの出力ディレクトリを分離します。次のようになります。
build/
|--classes/
|--resources/
run
タスクを使用する場合、classpath
はsourceSets.main.runtimeClasspath
の値です。この値には両方のディレクトリが含まれますが、クラスパスが機能するため、これは機能します。クラスパスは単なる1つの巨大なモジュールであると考えることができます。
ただし、技術的にはresources
内のファイルはclasses
内のモジュールに属していないため、モジュールパスを使用する場合、これは機能しません。 resources
がモジュールの一部であることをモジュールシステムに伝える方法が必要です。幸い、 --patch-module
があります。このオプションは(Java --help-extra
から引用):
jARファイルまたはディレクトリ内のクラスとリソースでモジュールをオーバーライドまたは拡張します。
また、次の形式になります(;
セパレーターはプラットフォームに依存すると想定しています)。
--patch-module <module>=<file>(;<file>)*
モジュールが自身のリソースにアクセスできるようにするには、run
タスクを次のように構成します。
run {
input.property('moduleName', moduleName)
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--patch-module', "$moduleName=" + files(sourceSets.main.output.resourcesDir).asPath,
'--module', "$moduleName/$mainClassName"
]
classpath = files()
}
}
これが私のやり方ですが、これまでのところかなりうまくいきました。
しかし、Gradleからアプリケーションを起動するときに、外部モジュールがモジュールのリソースにアクセスすることをどのように許可しますか?これはもう少し複雑になります。
外部モジュールがリソースにアクセスすることを許可する場合、モジュールはopens
でなければなりません( Eng.Fouadの回答 を参照)少なくとも読み取りモジュールへのリソースのパッケージ(これのみ) encapsulated リソースに適用されます)。ただし、これを発見すると、コンパイル警告とランタイムエラーが発生します。
opens
しようとしているためです。opens
ディレクティブを宣言したパッケージを見つけられないためです。--patch-module
オプションでも発生します。パッチを適用する前に、モジュールシステムが何らかの整合性チェックを行うと思います。注:「リソースのみ」とは、.Java
/.class
ファイルがないパッケージを意味します。
コンパイル警告を修正するには、compileJava
タスク内で--patch-module
を再度使用する必要があります。今回は、出力ディレクトリではなく、リソースのソースディレクトリを使用します。
compileJava {
inputs.property('moduleName', moduleName)
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
'--patch-module', "$moduleName=" + files(sourceSets.main.resources.srcDirs).asPath,
'--module-version', "$version"
]
}
}
実行時エラーには、いくつかのオプションがあります。最初のオプションは、リソース出力ディレクトリをクラスの出力ディレクトリと「マージ」することです。
sourceSets {
main.output.resourcesDir = main.Java.outputDir
}
jar {
// I've had bad experiences when "merging" output directories
// where the jar task ends up creating duplicate entries in the JAR.
// Use this option to combat that.
duplicateStrategy = DuplicatesStrategy.EXCLUDE
}
2番目のオプションは、展開されたディレクトリではなくJARファイルを実行するようにrun
タスクを構成することです。これは、最初のオプションと同様に、クラスとリソースを同じ場所に結合し、リソースがモジュールの一部であるため機能します。
run {
dependsOn += jar
inputs.property('moduleName', moduleName)
doFirst {
// add JAR file and runtime classpath. The runtime classpath includes the output of the main source set
// so we remove that to avoid having two of the same module on the modulepath
def modulepath = files(jar.archivePath) + (sourceSets.main.runtimeClasspath - sourceSets.main.output)
jvmArgs = [
'--module-path', modulepath.asPath,
'--module', "$moduleName/$mainClassName"
]
classpath = files()
}
}
これらのオプションはどちらも、run
タスクで--patch-module
を使用する代わりに使用できます(この回答の最初の部分で説明しています)。
おまけとして、これが--main-class
属性をモジュラーJARに追加する方法です。
jar {
inputs.property('mainClassName', mainClassName)
doLast {
exec {
executable = 'jar'
args = [
'--update', '--file', "$archivePath",
'--main-class', "$mainClassName"
]
}
}
}
これにより、Java -m module.name
ではなくJava -m module.name/some.package.Main
を使用できます。また、run
タスクがJARを実行するように構成されている場合は、次のように変更できます。
'--module', "$moduleName/$mainClassName"
に:
'--module', "$moduleName"
P.S。これを行うより良い方法がある場合はお知らせください
@Slawの回答(彼に感謝)のほかに、呼び出し元のモジュールへのリソースを含むパッケージを開く必要がありました。次のように(moduleone.name module-info.Java
):
opens io.fouad.packageone to moduletwo.name;
それ以外の場合、次はnull
を返します。
A.class.getResource("/io/fouad/packageone/logging.properties");
クラスA
はモジュールmoduletwo.name
にあり、ファイルlogging.properties
はモジュールmoduleone.name
内にあることを考慮してください。
または、moduleone.name
は、リソースを返すユーティリティメソッドを公開できます。
public static URL getLoggingConfigFileAsResource()
{
return A.class.getResource("/io/fouad/packageone/logging.properties");
}