Gradle の dependencies で指定する compile, api, implementation についての勉強メモ。
compile は非推奨
Gradle 3.4 で Java Library Plugin が追加されたことで、 dependencies で compile を使用することは非推奨となってたらしい(runtime, testCompile, testRuntime も)。
4.7 の Java Plugin の説明では、ガッツリ Deprecated と書かれている。
(4.6 のドキュメントだと Deprecated って書いてないけど、明確に非推奨となったのは最近?)
代わりに implementation と api を使用することが推奨されている。
compile と implementation の違い
compile の場合
実装
|-settings.gradle
|-build.gradle
|
|-foo/
| |-build.gradle
| `-src/main/java/foo/
| `-Foo.java
|
`-bar/
|-build.gradle
`-src/main/java/bar/
`-Bar.java
include 'foo', 'bar'
subprojects {
apply plugin: 'java'
sourceCompatibility = 10
targetCompatibility = 10
compileJava.options.encoding = 'UTF-8'
repositories {
mavenCentral()
}
}
dependencies {
compile 'org.apache.commons:commons-lang3:3.7'
}
package bar;
import org.apache.commons.lang3.RandomStringUtils;
public class Bar {
public void hello() {
System.out.println("Bar: " + RandomStringUtils.random(10, "0123456789"));
}
}
apply plugin: 'application'
mainClassName = 'foo.Foo'
dependencies {
compile project(':bar')
}
package foo;
import bar.Bar;
import org.apache.commons.lang3.RandomStringUtils;
public class Foo {
public static void main(String... args) {
new Bar().hello();
System.out.println("Foo: " + RandomStringUtils.random(10, "0123456789"));
}
}
-
foo,barという2つのサブプロジェクトから成る -
barはcommons-lang3に依存している -
fooはbarプロジェクトに依存している - それぞれのプロジェクトで
commons-lang3を利用している
実行結果
> gradle :foo:run
Bar: 3803159716
Foo: 6423224304
implementation を使った場合
実装
dependencies {
implementation 'org.apache.commons:commons-lang3:3.7'
}
apply plugin: 'application'
mainClassName = 'foo.Foo'
dependencies {
implementation project(':bar')
}
実行結果
> gradle :foo:run
...\foo\src\main\java\foo\Foo.java:4: エラー: パッケージorg.apache.commons.lang3は存在しません
import org.apache.commons.lang3.RandomStringUtils;
^
...\foo\src\main\java\foo\Foo.java:10: エラー: シンボルを見つけられません
System.out.println("Foo: " + RandomStringUtils.random(10, "0123456789"));
^
シンボル: 変数 RandomStringUtils
場所: クラス Foo
エラー2個
...
説明
# compile
[foo] --compile--> [bar] --compile--> [commons-lang3]
[foo] - ok -> [bar] - ok -> [commons-lang3]
| ^
| |
+------------ ok ---------------+
# implementation
[foo] --implementation--> [bar] --implementation--> [commons-lang3]
[foo] - ok -> [bar] - ok -> [commons-lang3]
| x
| |
+------------ ng ---------------+
-
compileで指定した依存関係は伝播する-
barプロジェクトでcommons-lang3をcompileで指定した場合、fooプロジェクトでbarプロジェクトを依存関係に指定すると、fooプロジェクトはcommons-lang3にも依存するようになる
-
-
implementationで指定した依存関係は伝播しない-
barプロジェクトでcommons-lang3をimplementationで指定した場合、fooプロジェクトでbarプロジェクトを依存関係に指定しても、fooプロジェクトはcommons-lang3に依存しない(fooプロジェクトはcommons-lang3を使用できない)
-
依存関係を伝播させる
実装
apply plugin: 'java-library'
dependencies {
api 'org.apache.commons:commons-lang3:3.7'
}
実行結果
$ gradle :foo:run
Bar: 7783742303
Foo: 6741510207
説明
apply plugin: 'java-library'
dependencies {
api 'org.apache.commons:commons-lang3:3.7'
}
-
compileのように依存関係を伝播させるには、apiで依存関係を指定する-
apiは Java Library Plugin を追加することで利用できるようになる - Java Library Plugin を使うには、
java-libraryでプラグインを追加する
-
メリット
公式ドキュメントでは、 compile ではなく implementation を使うことについて、次のようなメリットがあると説明している。
- コンパイル時に依存関係が利用側のどこにも漏れることがない。
従って、意図しない推移的な依存関係が発生することがない。 - 取り除かれたクラスパスによってコンパイルがより早くなる。
-
implementationで指定した依存対象が変更されても、利用する側はリコンパイルの必要がない。 - 新しい Maven プラグインと合わせて使うと、コンパイル時に必要になるライブラリと実行時に必要になるライブラリを明確に分けた POM ファイルを生成するようになり、綺麗な公開ができる(正直、 Maven プラグイン使ってないのでよくわかってない)
要するに、 compile を使うと次のような問題があった。
- 依存関係が全て推移的に伝播していくため、依存関係が不必要に拡大してしまっていた
- 内部 API のような外部に漏らしたくない依存関係まで広がっていた
これを implementation で定義することで、依存関係の不必要な拡大を防ぎ、本当に必要な依存関係だけを api で推移させられるようになる。
また、 implementation が依存関係を伝播させなくなることで、リコンパイルの頻度も減らせられるようになるメリットがある。
implementation と api の使い分け
以下は個人的な見解。
- 基本は
implementationで宣言する - どうしても利用側に伝播させないといけない場合だけ
apiで利用側に公開する- 可能なら自作 API でラップすることで、使用しているライブラリへの直接の依存を無くせれたらベストだと思う
- ライブラリの差し替えが容易になる
- 自作 API で利用方法を制限することで、意図しない使われ方・間違った利用方法を減らせられる
- ただ、自作 API でのラップが大変で費用対効果が薄いなら
apiで公開するのもありかと思う
- 可能なら自作 API でラップすることで、使用しているライブラリへの直接の依存を無くせれたらベストだと思う
まとめ
configuration |
依存関係の伝播 | 定義されているプラグイン |
|---|---|---|
compile |
する | Java Plugin |
implementation |
しない | Java Plugin |
api |
する | Java Library Plugin |