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 插件将其一些任务附加到基础插件(Java 插件自动应用)定义的生命周期任务,并且还添加了一些其他生命周期任务
assemble
-
依赖于:
jar
聚合任务,组装项目中的所有归档文件。此任务由基础插件添加。
check
-
依赖于:
test
执行验证任务(例如运行测试)的聚合任务。一些插件会将自己的验证任务添加到
check
。如果您希望自定义Test
任务在完全构建时执行,也应将其附加到此生命周期任务。此任务由基础插件添加。 build
-
依赖于:
check
,assemble
执行项目完整构建的聚合任务。此任务由基础插件添加。
buildNeeded
-
依赖于:
build
,以及testRuntimeClasspath
配置中作为依赖项的所有项目中的buildNeeded
任务。执行项目及其所有依赖项目的完整构建。
buildDependents
-
依赖于:
build
,以及所有在其testRuntimeClasspath
配置中将此项目作为依赖项的项目中的buildDependents
任务执行项目及其所有依赖于它的项目的完整构建。
buildConfigName
— 任务规则-
依赖于:所有生成附加到指定 — ConfigName — 配置的工件的任务
组装指定配置的工件。此规则由基础插件添加。
下图显示了这些任务之间的关系。

项目布局
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
-
表示项目主源集在编译时和运行时都需要的依赖项(即,仅实现依赖项)。
compileOnly
-
表示仅在编译时需要,不包含在运行时类路径中的依赖项(即,仅编译时依赖项,运行时不使用)。
runtimeOnly
-
表示仅在运行时需要,不包含在编译类路径中的依赖项(即,仅运行时需要的依赖项)。
testImplementation
扩展implementation
-
表示项目测试源集在编译时和运行时都需要的依赖项(即,仅用于测试的实现依赖项)。
testCompileOnly
-
表示仅在项目测试源集编译时需要,不包含在运行时类路径中的依赖项(即,仅用于编译测试的额外依赖项,运行时不使用)。
testRuntimeOnly
扩展runtimeOnly
-
表示仅在项目测试源集运行时需要的依赖项(即,仅用于运行测试的运行时依赖项)。
annotationProcessor
-
表示在项目源代码编译期间使用的注解处理器(即,编译期间使用的注解处理器)。
可解析配置
compileClasspath
扩展compileOnly, implementation
-
表示编译主源时使用的类路径,包括来自
compileOnly
和implementation
配置的依赖项。由任务compileJava
使用。 runtimeClasspath
扩展runtimeOnly, implementation
-
表示运行主源时使用的类路径,包括来自
runtimeOnly
和implementation
配置的依赖项。由任务run
使用。 testCompileClasspath
扩展testCompileOnly, testImplementation
-
表示编译测试源时使用的类路径,包括来自
testCompileOnly
和testImplementation
配置的依赖项。由任务compileTestJava
使用。 testRuntimeClasspath
扩展testRuntimeOnly, testImplementation
-
表示运行测试源时使用的类路径,包括来自
testRuntimeOnly
和testImplementation
配置的依赖项。由任务test
使用。
以下图表分别显示了主源集和测试源集的依赖配置。您可以使用此图例来解释颜色
-
蓝色背景 — 您可以针对该配置声明依赖项。
-
绿色背景 — 该配置供任务使用,而不是供您声明依赖项。
-
灰色背景 — 一个任务。


对于您添加到项目的每个源集,Java 插件都会添加以下依赖配置
源集依赖配置
sourceSetImplementation
-
给定源集的编译时依赖项。由
sourceSetCompileClasspath, sourceSetRuntimeClasspath
使用。 sourceSetCompileOnly
-
给定源集的仅编译时依赖项,运行时不使用。
sourceSetCompileClasspath
扩展sourceSetCompileOnly, sourceSetImplementation
-
编译类路径,编译源时使用。由
compileSourceSetJava
使用。 sourceSetAnnotationProcessor
-
此源集编译期间使用的注解处理器。
sourceSetRuntimeOnly
-
给定源集的仅运行时依赖项。
sourceSetRuntimeClasspath
扩展sourceSetRuntimeOnly, sourceSetImplementation
-
运行时类路径包含实现元素以及仅运行时元素。
贡献的扩展
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 并创建一个带有 artifact
-javadoc.jar
的变体javadocElements
,该变体将成为发布的一部分。 withSourcesJar()
-
自动打包源代码并创建一个带有 artifact
-sources.jar
的变体sourceElements
,该变体将成为发布的一部分。
目录属性
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
-
包含项目的源集。默认值:非空 SourceSetContainer
String archivesBaseName
-
用于归档文件(例如 JAR 或 ZIP 文件)的基本名称。默认值:
projectName
Manifest manifest
-
要包含在所有 JAR 文件中的清单。默认值:空清单。
测试
有关详细信息,请参阅Java & JVM 项目测试章节。
发布
components.java
-
用于发布由
jar
任务创建的生产 JAR 的 SoftwareComponent。此组件包括 JAR 的运行时依赖信息。
另请参阅java
扩展。
增量 Java 编译
Gradle 附带了一个复杂的增量 Java 编译器,默认情况下处于活动状态。
这为您带来了以下好处
-
增量构建速度快得多。
-
更改的类文件数量最少。不需要重新编译的类在输出目录中保持不变。一个真正有用的场景是使用 JRebel——更改的输出类越少,JVM 刷新类的速度就越快。
为了帮助您理解增量编译的工作原理,下面提供了一个高级概述
-
Gradle 将重新编译所有受更改影响的类。
-
如果一个类已被更改或依赖于另一个受影响的类,则该类为受影响的。这无论其他类是在同一项目中、另一个项目中还是外部库中定义都有效。
-
一个类的依赖项是通过其字节码中的类型引用或通过编译器插件进行符号分析来确定的。
-
由于源保留注解在字节码中不可见,因此更改源保留注解将导致完全重新编译。
-
您可以通过应用松散耦合等良好软件设计原则来提高增量编译性能。例如,如果您在具体类及其依赖项之间放置一个接口,则只有当接口更改时,依赖类才会重新编译,而不是当实现更改时。
-
类分析在项目目录中缓存,因此清理结账后的第一次构建可能会较慢。考虑在您的构建服务器上关闭增量编译器。
-
类分析也是存储在构建缓存中的输出,这意味着如果从构建缓存中获取编译输出,则增量编译分析也将被获取,并且下一次编译将是增量编译。
增量注解处理
从 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 and 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 属性。