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

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


对于您添加到项目的每个 source set,Java 插件都会添加以下依赖配置
SourceSet 依赖配置
sourceSetImplementation
-
给定 source set 的编译时依赖项。由
sourceSetCompileClasspath, sourceSetRuntimeClasspath
使用。 sourceSetCompileOnly
-
给定 source set 的仅编译时依赖项,运行时不使用。
sourceSetCompileClasspath
扩展sourceSetCompileOnly, sourceSetImplementation
-
编译 classpath,用于编译源代码。由
compileSourceSetJava
使用。 sourceSetAnnotationProcessor
-
在此 source set 编译期间使用的注解处理器。
sourceSetRuntimeOnly
-
给定 source set 的仅运行时依赖项。
sourceSetRuntimeClasspath
扩展sourceSetRuntimeOnly, sourceSetImplementation
-
运行时 classpath 包含实现的元素以及仅运行时元素。
贡献的扩展
Java 插件将 java
扩展添加到项目中。这允许在专用的 DSL 块内配置许多与 Java 相关的属性。
java
扩展配置工具链java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
以下是在 java
扩展内可用的属性和 DSL 函数列表,并附有简短说明。
工具链和兼容性
toolchain
-
供使用 JVM 工具(例如编译和执行)的 Task 使用的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
-
用于生成测试报告的目录名称,相对于报告目录。默认值:
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
-
包含项目的 source sets。默认值:非空 SourceSetContainer
String archivesBaseName
-
用于归档文件(例如 JAR 或 ZIP 文件)的基本名称。默认值:
projectName
Manifest manifest
-
要包含在所有 JAR 文件中的 manifest。默认值:一个空的 manifest。
约定属性(已弃用)
Java 插件为项目添加了许多约定属性。您可以在构建脚本中使用这些属性,就好像它们是项目对象的属性一样。这些属性已弃用,并由上面描述的扩展取代。有关它们的更多信息,请参阅 JavaPluginConvention DSL 文档。
测试
有关更多详细信息,请参阅 测试 Java 和 JVM 项目 章节。
发布
components.java
-
用于发布由
jar
Task 创建的生产 JAR 的 SoftwareComponent。此组件包含 JAR 的运行时依赖项信息。
另请参阅 java
扩展。
增量 Java 编译
Gradle 配备了复杂的增量 Java 编译器,默认情况下处于活动状态。
这为您带来以下好处
-
增量构建速度更快。
-
更改的类文件数量尽可能少。不需要重新编译的类在输出目录中保持不变。一个真正有用的示例场景是使用 JRebel — 更改的输出类越少,JVM 可以更快地使用刷新的类。
为了帮助您了解增量编译的工作原理,以下内容提供了高级概述
-
Gradle 将重新编译所有受更改影响的类。
-
如果一个类已更改或它依赖于另一个受影响的类,则该类被影响。无论另一个类是在同一项目、另一个项目甚至外部库中定义,这都有效。
-
类的依赖项是从其字节码中的类型引用或通过编译器插件进行的符号分析来确定的。
-
由于 source-retention 注解在字节码中不可见,因此对 source-retention 注解的更改将导致完全重新编译。
-
您可以通过应用良好的软件设计原则(如松耦合)来提高增量编译性能。例如,如果您在具体类及其依赖项之间放置一个接口,则仅当接口更改时才重新编译依赖类,而不是在实现更改时重新编译。
-
类分析缓存在项目目录中,因此在干净签出后的第一次构建可能会较慢。考虑在您的构建服务器上关闭增量编译器。
-
类分析也是存储在构建缓存中的输出,这意味着如果从构建缓存中获取编译输出,则增量编译分析也将是,并且下一次编译将是增量的。
增量注解处理
从 Gradle 4.7 开始,增量编译器也支持增量注解处理。所有注解处理器都需要选择启用此功能,否则它们将触发完全重新编译。
作为用户,您可以在 --info
日志中查看哪些注解处理器正在触发完全重新编译。如果在编译任务上配置了自定义的 executable
或 javaHome
,则增量注解处理将被禁用。
使注解处理器支持增量编译
请首先查看 增量 Java 编译,因为增量注解处理是基于它构建的。
Gradle 支持两种常见的注解处理器类别的增量编译:“隔离型”和“聚合型”。请查阅以下信息以确定哪个类别适合您的处理器。
然后,您可以使用处理器 META-INF 目录中的文件注册您的处理器以进行增量编译。格式为每行一个处理器,处理器类的完全限定名称及其不区分大小写的类别之间用逗号分隔。
示例:注册增量注解处理器
org.gradle.EntityProcessor,isolating org.gradle.ServiceRegistryProcessor,dynamic
如果您的处理器只能在运行时决定它是否是增量的,您可以在 META-INF 描述符中将其声明为“动态”类型,并使用 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 和 Henson |
不适用 |
|
不适用 |
||
不适用 |
||
不适用 |
||
Requery |
不适用 |
|
不适用 |
||
EclipseLink |
不适用 |
|
不适用 |
||
Immutables |
不适用 |
|
2.2.0 功能开关支持,2.3.0-alpha02 默认启用 |
||
不适用 |
||
不适用 |
||
DBFlow |
不适用 |
|
AndServer |
不适用 |
|
不适用 |
||
不适用 |
||
不适用 |
||
不适用 |
||
隐藏在功能开关之后 |
||
不适用 |
编译避免
如果依赖项目以 ABI 兼容的方式进行了更改(仅其私有 API 发生了更改),则 Java 编译任务将是最新的。这意味着如果项目 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 插件利用 变体感知解析 来处理使用的依赖项。它们还安装了一组属性兼容性和消除歧义规则,以 配置 JVM 生态系统特有的 Gradle 属性。