A study note about compile, ʻapi, ʻimplementation specified by dependencies in Gradle.
Java Library Plugin has been added in Gradle 3.4 (https://docs.gradle.org/3.4/release-notes. html # the-java-library-plugin), so it seems that using compile with dependencies was deprecated (also runtime, testCompile, testRuntime).
In 4.7 Description of Java Plugin, it is written as Deprecated.
(4.6 documentation doesn't say Deprecated, but it's definitely deprecated. Recently?)
It is recommended to use ʻimplementation and ʻapi instead.
Implementation
Project structure
|-settings.gradle
|-build.gradle
|
|-foo/
| |-build.gradle
| `-src/main/java/foo/
| `-Foo.java
|
`-bar/
|-build.gradle
`-src/main/java/bar/
`-Bar.java
/settings.gradle
include 'foo', 'bar'
/build.gradle
subprojects {
apply plugin: 'java'
sourceCompatibility = 10
targetCompatibility = 10
compileJava.options.encoding = 'UTF-8'
repositories {
mavenCentral()
}
}
/bar/build.gradle
dependencies {
compile 'org.apache.commons:commons-lang3:3.7'
}
Bar.java
package bar;
import org.apache.commons.lang3.RandomStringUtils;
public class Bar {
public void hello() {
System.out.println("Bar: " + RandomStringUtils.random(10, "0123456789"));
}
}
/foo/build.gradle
apply plugin: 'application'
mainClassName = 'foo.Foo'
dependencies {
compile project(':bar')
}
Foo.java
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"));
}
}
--Consists of two subprojects, foo and bar
--bar depends on commons-lang3
--foo depends on the bar project
--Each project uses commons-lang3
Execution result
> gradle :foo:run
Bar: 3803159716
Foo: 6423224304
Implementation
/bar/build.gradle
dependencies {
implementation 'org.apache.commons:commons-lang3:3.7'
}
/foo/build.gradle
apply plugin: 'application'
mainClassName = 'foo.Foo'
dependencies {
implementation project(':bar')
}
Execution result
> gradle :foo:run
...\foo\src\main\java\foo\Foo.java:4:error:Package org.apache.commons.lang3 does not exist
import org.apache.commons.lang3.RandomStringUtils;
^
...\foo\src\main\java\foo\Foo.java:10:error:Can't find symbol
System.out.println("Foo: " + RandomStringUtils.random(10, "0123456789"));
^
symbol:Variable RandomStringUtils
place:Class Foo
2 errors
...
Dependency relationship
# 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 ---------------+
--The dependency specified by compile is propagated
--If you specify commons-lang3 in compile in the bar project and specify the bar project as a dependency in the foo project, the foo project also depends on commons-lang3 Will be
--The dependency specified by ʻimplementationis not propagated --If you specifycommons-lang3 with ʻimplementation in the bar project, the foo project depends on commons-lang3 even if you specify the bar project as a dependency in the foo project. No (foo project cannot use commons-lang3)
Implementation
/bar/build.gradle
apply plugin: 'java-library'
dependencies {
api 'org.apache.commons:commons-lang3:3.7'
}
Execution result
$ gradle :foo:run
Bar: 7783742303
Foo: 6741510207
/bar/build.gradle
apply plugin: 'java-library'
dependencies {
api 'org.apache.commons:commons-lang3:3.7'
}
--To propagate dependencies like compile, specify the dependencies with ʻapi --ʻApi becomes available by adding Java Library Plugin
--To use the Java Library Plugin, add the plugin with java-library
The official documentation explains that using ʻimplementation instead of compile` has the following benefits:
In short, using compile had the following problems.
--Since all the dependencies are transitively propagated, the dependencies have expanded unnecessarily. --It has spread to dependencies that you don't want to leak to the outside, such as internal APIs.
By defining this with ʻimplementation, it is possible to prevent unnecessary expansion of dependencies and to transition only the dependencies that are really necessary with ʻapi.
It also has the advantage that the frequency of recompilation can be reduced by preventing ʻimplementation` from propagating dependencies.
and ʻapiThe following is my personal opinion.
--Basically declared with ʻimplementation --Publish to the user side with ʻapi only when it is absolutely necessary to propagate it to the user side
--If possible, it would be best if you could eliminate the direct dependence on the library you are using by wrapping it in your own API.
--Easy to replace the library
--By limiting the usage with your own API, you can reduce unintended usage and incorrect usage.
――However, if wrapping with your own API is difficult and cost-effective, I think it may be possible to publish it with ʻapi`.
configuration |
Dependency propagation | Defined plugin |
|---|---|---|
compile |
To do | Java Plugin |
implementation |
do not do | Java Plugin |
api |
To do | Java Library Plugin |