Java 插件为项目添加了 Java 编译以及测试和打包能力。它作为许多其他 JVM 语言 Gradle 插件的基础。你可以在构建 Java 项目章节找到关于 Java 插件的全面介绍和概述。
如上所述,该插件为处理 JVM 项目添加了基本的构建模块。它的功能集已经被其他插件取代,这些插件根据你的项目类型提供了更多功能。你应该考虑使用 |
用法
要使用 Java 插件,请在构建脚本中包含以下内容:
plugins {
java
}
plugins {
id 'java'
}
任务
Java 插件向你的项目添加了许多任务,如下所示。
compileJava
— JavaCompile-
依赖于:所有对编译类路径有贡献的任务,包括通过项目依赖关系位于类路径上的项目的
jar
任务使用 JDK 编译器编译生产环境的 Java 源文件。
processResources
— ProcessResources-
将生产环境资源复制到生产环境资源目录。
classes
-
依赖于:
compileJava
,processResources
这是一个聚合任务,仅依赖于其他任务。其他插件可能会向其附加额外的编译任务。
compileTestJava
— JavaCompile-
依赖于:
classes
,以及所有对测试编译类路径有贡献的任务使用 JDK 编译器编译测试环境的 Java 源文件。
processTestResources
— Copy-
将测试资源复制到测试资源目录。
testClasses
-
依赖于:
compileTestJava
,processTestResources
这是一个聚合任务,仅依赖于其他任务。其他插件可能会向其附加额外的测试编译任务。
jar
— Jar-
依赖于:
classes
基于附加到
main
源集的类和资源组装生产环境的 JAR 文件。 javadoc
— Javadoc-
依赖于:
classes
使用 Javadoc 为生产环境的 Java 源文件生成 API 文档。
test
— Test-
依赖于:
testClasses
,以及所有生成测试运行时类路径的任务使用 JUnit 或 TestNG 运行单元测试。
clean
— Delete-
删除项目构建目录。
cleanTaskName
— Delete-
删除指定任务创建的文件。例如,
cleanJar
将删除由jar
任务创建的 JAR 文件,cleanTest
将删除由test
任务创建的测试结果。
源集任务
对于你添加到项目的每个源集,Java 插件会添加以下任务:
compileSourceSetJava
— JavaCompile-
依赖于:所有对该源集编译类路径有贡献的任务
使用 JDK 编译器编译给定源集的 Java 源文件。
processSourceSetResources
— Copy-
将给定源集的资源复制到资源目录。
sourceSetClasses
— Task-
依赖于:
compileSourceSetJava
,processSourceSetResources
准备给定源集的类和资源以进行打包和执行。某些插件可能为此源集添加额外的编译任务。
生命周期任务
Java 插件将其一些任务附加到 Base 插件(Java 插件会自动应用它)定义的生命周期任务,同时还添加了一些其他生命周期任务:
assemble
-
依赖于:
jar
聚合任务,组装项目中的所有档案。此任务由 Base 插件添加。
check
-
依赖于:
test
聚合任务,执行验证任务,例如运行测试。一些插件会向
check
添加它们自己的验证任务。如果你希望自定义的Test
任务在完整构建中执行,也应将它们附加到此生命周期任务。此任务由 Base 插件添加。 build
-
依赖于:
check
,assemble
聚合任务,执行项目的完整构建。此任务由 Base 插件添加。
buildNeeded
-
依赖于:
build
,以及在testRuntimeClasspath
配置中作为依赖项的所有项目的buildNeeded
任务。执行项目及其所有依赖项目的完整构建。
buildDependents
-
依赖于:
build
,以及在它们的testRuntimeClasspath
配置中将此项目作为依赖项的所有项目的buildDependents
任务执行项目及其所有依赖于它的项目的完整构建。
buildConfigName
— 任务规则-
依赖于:所有生成附加到指定(ConfigName)配置的制品的任务
组装指定配置的制品。此规则由 Base 插件添加。
下图显示了这些任务之间的关系。

项目布局
Java 插件假设的项目布局如下所示。这些目录都不需要存在或包含任何内容。Java 插件会编译它找到的任何东西,并处理任何缺失的部分。
src/main/java
-
生产环境 Java 源文件。
src/main/resources
-
生产环境资源,例如 XML 和属性文件。
src/test/java
-
测试环境 Java 源文件。
src/test/resources
-
测试资源。
src/sourceSet/java
-
名为 sourceSet 的源集的 Java 源文件。
src/sourceSet/resources
-
名为 sourceSet 的源集的资源。
更改项目布局
你可以通过配置相应的源集来配置项目布局。这将在以下部分详细讨论。这是一个简单的示例,更改了主要的 Java 和资源源目录。
sourceSets {
main {
java {
setSrcDirs(listOf("src/java"))
}
resources {
setSrcDirs(listOf("src/resources"))
}
}
}
sourceSets {
main {
java {
srcDirs = ['src/java']
}
resources {
srcDirs = ['src/resources']
}
}
}
源集
该插件添加了以下源集:
main
-
包含项目的生产源代码,这些代码将被编译并组装成一个 JAR。
test
-
包含你的测试源代码,这些代码将使用 JUnit 或 TestNG 进行编译和执行。这些通常是单元测试,但你可以将任何测试包含在此源集中,只要它们共享相同的编译和运行时类路径。
源集属性
下表列出了一些重要的源集属性。你可以在 SourceSet 的 API 文档中找到更多详细信息。
name
— (只读)String
-
源集的名称,用于标识它。
output
— (只读) SourceSetOutput-
源集的输出文件,包含其编译后的类和资源。
output.classesDirs
— (只读) FileCollection-
默认值:
layout.buildDirectory.dir("classes/java/$name")
,例如 build/classes/java/main生成此源集的类的目录。可能包含其他 JVM 语言的目录,例如 build/classes/kotlin/main。
output.resourcesDir
—File
-
默认值:
layout.buildDirectory.dir("resources/$name")
,例如 build/resources/main生成此源集的资源的目录。
compileClasspath
— FileCollection-
默认值:
${name}CompileClasspath
配置编译此源集源文件时使用的类路径。
annotationProcessorPath
— FileCollection-
默认值:
${name}AnnotationProcessor
配置编译此源集源文件时使用的处理器路径。
runtimeClasspath
— FileCollection-
默认值:
$output
,${name}RuntimeClasspath
配置执行此源集的类时使用的类路径。
java
— (只读) SourceDirectorySet-
此源集的 Java 源文件。仅包含在 Java 源目录中找到的
.java
文件,并排除所有其他文件。 java.srcDirs
—Set<File>
-
默认值:
src/$name/java
,例如 src/main/java包含此源集 Java 源文件的源目录。你可以将其设置为 本节中描述的任何值。
java.destinationDirectory
—DirectoryProperty
-
默认值:
layout.buildDirectory.dir("classes/java/$name")
,例如 build/classes/java/main生成编译后的 Java 源文件的目录。你可以将其设置为 本节中描述的任何值。
resources
— (只读) SourceDirectorySet-
此源集的资源。仅包含资源,并排除在资源目录中找到的任何
.java
文件。其他插件(例如 Groovy 插件)会从此集合中排除其他类型的文件。 resources.srcDirs
—Set<File>
-
默认值:
[src/$name/resources]
包含此源集资源的目录。你可以将其设置为 本节中描述的任何类型的值。
allJava
— (只读) SourceDirectorySet-
默认值:与
java
属性相同此源集的所有 Java 文件。某些插件(例如 Groovy 插件)会向此集合添加额外的 Java 源文件。
allSource
— (只读) SourceDirectorySet-
默认值:
resources
和java
属性中所有内容的总和此源集的任何语言的所有源文件。这包括所有资源文件和所有 Java 源文件。某些插件(例如 Groovy 插件)会向此集合添加额外的源文件。
其他一些简单的源集示例
添加一个包含源集类的 JAR
tasks.register<Jar>("intTestJar") {
from(sourceSets["intTest"].output)
}
tasks.register('intTestJar', Jar) {
from sourceSets.intTest.output
}
为源集生成 Javadoc
tasks.register<Javadoc>("intTestJavadoc") {
source(sourceSets["intTest"].allJava)
classpath = sourceSets["intTest"].compileClasspath
}
tasks.register('intTestJavadoc', Javadoc) {
source sourceSets.intTest.allJava
classpath = sourceSets.intTest.compileClasspath
}
添加一个测试套件以运行源集中的测试
tasks.register<Test>("intTest") {
testClassesDirs = sourceSets["intTest"].output.classesDirs
classpath = sourceSets["intTest"].runtimeClasspath
}
tasks.register('intTest', Test) {
testClassesDirs = sourceSets.intTest.output.classesDirs
classpath = sourceSets.intTest.runtimeClasspath
}
依赖管理
Java 插件会向你的项目添加许多依赖配置,如下所示。然后,compileJava
和 test
等任务会使用这些配置中的一个或多个来获取相应的 文件并使用它们,例如将它们放到编译或运行时类路径上。
依赖配置
有关 有关 |
依赖声明配置
implementation
-
表示项目的 main 源集在编译时和运行时都需要依赖项(即,仅实现依赖项)。
compileOnly
-
表示仅在编译时需要且不包含在运行时类路径中的依赖项(即,仅编译时依赖项,运行时未使用)。
runtimeOnly
-
表示仅在运行时需要且不包含在编译类路径中的依赖项(即,仅运行时需要依赖项)。
testImplementation
扩展自implementation
-
表示项目的 test 源集在编译时和运行时都需要依赖项(即,测试的仅实现依赖项)。
testCompileOnly
-
表示项目的 test 源集仅在编译时需要且不包含在运行时类路径中的依赖项(即,仅用于编译测试的额外依赖项,运行时未使用)。
testRuntimeOnly
扩展自runtimeOnly
-
表示项目的 test 源集仅在运行时需要依赖项(即,用于运行测试的仅运行时依赖项)。
annotationProcessor
-
表示在编译项目源代码期间使用的注解处理器(即,编译期间使用的注解处理器)。
可解析配置
compileClasspath
扩展自compileOnly, implementation
-
表示编译 main 源时使用的类路径,包括来自
compileOnly
和implementation
配置的依赖项。由任务compileJava
使用。 runtimeClasspath
扩展自runtimeOnly, implementation
-
表示运行 main 源时使用的类路径,包括来自
runtimeOnly
和implementation
配置的依赖项。由任务run
使用。 testCompileClasspath
扩展自testCompileOnly, testImplementation
-
表示编译 test 源时使用的类路径,包括来自
testCompileOnly
和testImplementation
配置的依赖项。由任务compileTestJava
使用。 testRuntimeClasspath
扩展自testRuntimeOnly, testImplementation
-
表示运行 test 源时使用的类路径,包含 implementation 的元素以及 runtime only 的元素。由任务
test
使用。
以下图表分别显示了 main 和 test 源集的依赖配置。你可以使用此图例来解释颜色:
-
蓝色背景 — 你可以针对该配置声明依赖项。
-
绿色背景 — 该配置用于任务消费,而不是供你声明依赖项。
-
灰色背景 — 一个任务。


对于你添加到项目的每个源集,Java 插件会添加以下依赖配置:
源集依赖配置
sourceSetImplementation
-
给定源集的编译时依赖项。由
sourceSetCompileClasspath, sourceSetRuntimeClasspath
使用。 sourceSetCompileOnly
-
给定源集的仅编译时依赖项,运行时未使用。
sourceSetCompileClasspath
扩展自sourceSetCompileOnly, sourceSetImplementation
-
编译类路径,在编译源文件时使用。由
compileSourceSetJava
使用。 sourceSetAnnotationProcessor
-
编译此源集期间使用的注解处理器。
sourceSetRuntimeOnly
-
给定源集的仅运行时依赖项。
sourceSetRuntimeClasspath
扩展自sourceSetRuntimeOnly, sourceSetImplementation
-
运行时类路径包含 implementation 的元素以及 runtime only 的元素。
贡献的扩展
Java 插件向项目添加了 java
扩展。这允许在专门的 DSL 块中配置许多与 Java 相关的属性。
java
扩展配置工具链java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
下面列出了 java
扩展中可用的属性和 DSL 函数,并附有简短说明。
工具链和兼容性
toolchain
-
由使用 JVM 工具(如编译和执行)的任务使用的Java 工具链。默认值:构建 JVM 工具链。
JavaVersion sourceCompatibility
-
编译 Java 源文件时使用的 Java 版本兼容性。默认值:此扩展中工具链的语言版本。
请注意,在大多数情况下,使用工具链比使用兼容性设置更优。 JavaVersion targetCompatibility
-
生成类的目标 Java 版本。默认值:
sourceCompatibility
。
请注意,在大多数情况下,使用工具链比使用兼容性设置更优。
打包
withJavadocJar()
-
自动打包 Javadoc 并创建一个变体
javadocElements
,其制品为-javadoc.jar
,它将作为发布的一部分。 withSourcesJar()
-
自动打包源代码并创建一个变体
sourceElements
,其制品为-sources.jar
,它将作为发布的一部分。
目录属性
String reporting.baseDir
-
相对于构建目录的报告生成目录名称。默认值:
reports
(只读) File reportsDir
-
生成报告的目录。默认值:
reporting.baseDirectory
String testResultsDirName
-
相对于构建目录的测试结果 .xml 文件生成目录名称。默认值:
test-results
(只读) File testResultsDir
-
生成测试结果 .xml 文件的目录。默认值:
layout.buildDirectory.dir(testResultsDirName)
String testReportDirName
-
相对于 reports 目录的测试报告生成目录名称。默认值:
tests
(只读) File testReportDir
-
生成测试报告的目录。默认值:
reportsDir/testReportDirName
String libsDirName
-
相对于构建目录的库生成目录名称。默认值:
libs
(只读) File libsDir
-
生成库的目录。默认值:
layout.buildDirectory.dir(libsDirName)
String distsDirName
-
相对于构建目录的分发文件生成目录名称。默认值:
distributions
(只读) File distsDir
-
生成分发文件的目录。默认值:
layout.buildDirectory.dir(distsDirName)
String docsDirName
-
相对于构建目录的文档生成目录名称。默认值:
docs
(只读) File docsDir
-
生成文档的目录。默认值:
layout.buildDirectory.dir(docsDirName)
String dependencyCacheDirName
-
相对于构建目录的源依赖信息缓存目录名称。默认值:
dependency-cache
。
其他属性
(只读) SourceSetContainer sourceSets
-
包含项目的源集。默认值:非空 SourceSetContainer
String archivesBaseName
-
用于档案(如 JAR 或 ZIP 文件)的基名称。默认值:
projectName
Manifest manifest
-
包含在所有 JAR 文件中的清单。默认值:一个空的清单。
约定属性(已弃用)
Java 插件向项目添加了许多约定属性。你可以在构建脚本中像使用项目对象的属性一样使用这些属性。这些属性已弃用,并已被上面描述的扩展取代。有关它们的信息,请参阅 JavaPluginConvention DSL 文档。
测试
有关更多详细信息,请参阅Java 和 JVM 项目中的测试章节。
发布
components.java
-
一个 SoftwareComponent,用于发布由
jar
任务创建的生产环境 JAR。此组件包含 JAR 的运行时依赖信息。
另请参阅 java
扩展。
增量式 Java 编译
Gradle 带有一个复杂的增量式 Java 编译器,它默认是激活的。
这为您带来以下好处:
-
增量构建速度快得多。
-
更改的类文件数量最少。不需要重新编译的类在输出目录中保持不变。一个在这种情况下非常有用的示例是使用 JRebel — 输出类更改得越少,JVM 刷新类的速度就越快。
为了帮助您理解增量编译的工作原理,以下提供一个高层概览:
-
Gradle 将重新编译受更改影响的所有类。
-
如果一个类已被更改,或者它依赖于另一个受影响的类,则认为该类是受影响的。这无论另一类是在同一项目、另一个项目还是外部库中定义都适用。
-
一个类的依赖关系通过字节码中的类型引用或通过编译器插件进行符号分析来确定。
-
由于源保留注解在字节码中不可见,因此源保留注解的更改将导致完全重新编译。
-
您可以通过应用良好的软件设计原则(如松耦合)来提高增量编译的性能。例如,如果您在具体类及其依赖项之间放置一个接口,则仅当接口更改时才会重新编译依赖项,而不是当实现更改时。
-
类分析缓存在项目目录中,因此首次干净检出后的构建可能会较慢。考虑在构建服务器上关闭增量编译器。
-
类分析也是一个输出,存储在构建缓存中,这意味着如果编译输出是从构建缓存中获取的,那么增量编译分析也将被获取,并且下一次编译将是增量的。
增量注解处理
从 Gradle 4.7 开始,增量编译器也支持增量注解处理。所有注解处理器都需要选择启用此功能,否则它们将触发完全重新编译。
作为用户,您可以在 --info
日志中查看哪些注解处理器正在触发完全重新编译。如果在编译任务上配置了自定义的 executable
或 javaHome
,增量注解处理将被禁用。
使注解处理器增量化
请首先查看 增量 Java 编译,因为增量注解处理以此为基础。
Gradle 支持两种常见类型的注解处理器的增量编译:“独立的”(isolating)和“聚合的”(aggregating)。请参考以下信息来决定哪种类别适合您的处理器。
然后,您可以使用处理器 META-INF 目录中的文件注册您的处理器以进行增量编译。格式为每行一个处理器,包含处理器的完全限定名及其不区分大小写的类别,两者用逗号分隔。
示例:注册增量注解处理器
org.gradle.EntityProcessor,isolating org.gradle.ServiceRegistryProcessor,dynamic
如果您的处理器只能在运行时决定它是否是增量的,您可以将其在 META-INF 描述符中声明为“dynamic”,并在运行时使用 Processor#getSupportedOptions() 方法返回其真实类型。
示例:动态注册增量注解处理器
@Override
public Set<String> getSupportedOptions() {
return Collections.singleton("org.gradle.annotation.processing.aggregating");
}
这两种类别都有以下限制:
-
它们只能读取
CLASS
或RUNTIME
保留策略的注解。 -
只有当用户传递了
-parameters
编译器参数时,它们才能读取参数名。 -
它们必须使用 Filer API 生成文件。使用任何其他方式写入文件将导致后续静默失败,因为这些文件不会被正确清理。如果您的处理器这样做,它就不能是增量的。
-
它们不得依赖于特定于编译器的 API,如
com.sun.source.util.Trees
。Gradle 封装了处理 API,因此尝试转换为特定于编译器的类型将失败。如果您的处理器这样做,它就不能是增量的,除非您有一些回退机制。 -
如果它们使用 Filer#createResource,则
location
参数必须是 StandardLocation 中的这些值之一:CLASS_OUTPUT
、SOURCE_OUTPUT
或NATIVE_HEADER_OUTPUT
。任何其他参数都会禁用增量处理。
“独立的”注解处理器
这是最快的类别,它们独立地检查每个被注解的元素,为其创建生成的文件或验证消息。例如,一个 EntityProcessor
可以为每个使用 @Entity
注解的类型创建一个 <TypeName>Repository
。
示例:一个独立的注解处理器
Set<? extends Element> entities = roundEnv.getElementsAnnotatedWith(entityAnnotation);
for (Element entity : entities) {
createRepository((TypeElement) entity);
}
“独立的”处理器有以下附加限制:
-
它们必须基于从被注解类型 AST 可访问的信息做出所有决策(代码生成、验证消息)。这意味着您可以分析类型的超类、方法返回类型、注解等,甚至可以传递地分析。但您不能基于 RoundEnvironment 中不相关的元素做出决策。这样做会导致静默失败,因为后续会重新编译太少的文件。如果您的处理器需要基于本来不相关的元素的组合做出决策,请将其标记为“聚合的”。
-
它们必须为每个使用
Filer
API 生成的文件提供恰好一个源元素。如果提供了零个或多个源元素,Gradle 将重新编译所有源文件。
当源文件被重新编译时,Gradle 将重新编译由此源文件生成的所有文件。当源文件被删除时,由此源文件生成的文件也会被删除。
“聚合的”注解处理器
这些处理器可以将多个源文件聚合成一个或多个输出文件或验证消息。例如,一个 ServiceRegistryProcessor
可以创建一个包含一个方法的单一 ServiceRegistry
,该方法对应于每个使用 @Service
注解的类型。
示例:一个聚合的注解处理器
JavaFileObject serviceRegistry = filer.createSourceFile("ServiceRegistry");
Writer writer = serviceRegistry.openWriter();
writer.write("public class ServiceRegistry {");
for (Element service : roundEnv.getElementsAnnotatedWith(serviceAnnotation)) {
addServiceCreationMethod(writer, (TypeElement) service);
}
writer.write("}");
writer.close();
Gradle 将始终重新处理(但不重新编译)处理器注册的所有被注解文件。Gradle 将始终重新编译处理器生成的任何文件。
流行注解处理器的支持状态
许多流行的注解处理器支持增量注解处理(见下表)。请直接与注解处理器项目联系以获取最新的信息和文档。 |
注解处理器 | 支持版本 | 详情 |
---|---|---|
不适用 |
||
不适用 |
||
部分支持。 |
||
不适用 |
||
不适用 |
||
DataBinding |
隐藏在功能开关后 |
|
Dagger |
2.18 支持功能开关,2.24 默认启用 |
|
kapt |
隐藏在功能开关后 |
|
Toothpick |
不适用 |
|
Glide |
不适用 |
|
Android-State |
不适用 |
|
Parceler |
不适用 |
|
Dart and Henson |
不适用 |
|
不适用 |
||
不适用 |
||
不适用 |
||
Requery |
不适用 |
|
不适用 |
||
EclipseLink |
不适用 |
|
不适用 |
||
Immutables |
不适用 |
|
2.2.0 支持功能开关,2.3.0-alpha02 默认启用 |
||
不适用 |
||
不适用 |
||
DBFlow |
不适用 |
|
AndServer |
不适用 |
|
不适用 |
||
不适用 |
||
不适用 |
||
不适用 |
||
隐藏在功能开关后 |
||
不适用 |
避免编译
如果一个依赖的项目以 ABI 兼容的方式发生了变化(仅其私有 API 发生了变化),则 Java 编译任务将是最新(up-to-date)的。这意味着如果项目 A
依赖于项目 B
,并且 B
中的一个类以 ABI 兼容的方式发生了变化(通常仅改变方法体),那么 Gradle 将不会重新编译 A
。
以下是一些不影响公共 API 并被忽略的更改类型:
-
改变方法体
-
改变注释
-
添加、删除或改变私有方法、字段或内部类
-
添加、删除或改变资源
-
改变类路径中 jar 或目录的名称
-
重命名参数
由于实现细节对注解处理器很重要,因此必须在注解处理器路径上单独声明它们。Gradle 会忽略编译类路径上的注解处理器。
dependencies {
// The dagger compiler and its transitive dependencies will only be found on annotation processing classpath
annotationProcessor("com.google.dagger:dagger-compiler:2.44")
// And we still need the Dagger library on the compile classpath itself
implementation("com.google.dagger:dagger:2.44")
}
dependencies {
// The dagger compiler and its transitive dependencies will only be found on annotation processing classpath
annotationProcessor 'com.google.dagger:dagger-compiler:2.44'
// And we still need the Dagger library on the compile classpath itself
implementation 'com.google.dagger:dagger:2.44'
}
变体感知选择
整套 JVM 插件利用了 变体感知解析 来处理使用的依赖项。它们还安装了一组属性兼容性和消歧规则,以 配置 Gradle 属性 以适应 JVM 生态系统的具体情况。