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 specify
commons-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 ʻapi
The 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 |