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
— 任务-
依赖于:
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
等任务使用其中一个或多个配置来获取相应的文件并使用它们,例如将它们放在编译或运行时类路径上。
依赖配置
有关 default
和 archives
配置的信息,请参阅基础插件 参考文档。
有关 api
或 compileOnlyApi
配置的信息,请参阅Java 库插件 参考文档和Java 项目的依赖管理。
implementation
-
仅实现依赖项。
compileOnly
-
仅编译时依赖项,运行时不使用。
compileClasspath
扩展compileOnly, implementation
-
编译类路径,用于编译源代码。由任务
compileJava
使用。 annotationProcessor
-
编译期间使用的注释处理器。
runtimeOnly
-
仅运行时依赖项。
runtimeClasspath
扩展runtimeOnly, implementation
-
运行时类路径包含实现的元素,以及仅运行时元素。
testImplementation
扩展implementation
-
测试的仅实现依赖项。
testCompileOnly
-
仅用于编译测试的额外依赖项,运行时不使用。
testCompileClasspath
扩展testCompileOnly, testImplementation
-
测试编译类路径,用于编译测试源代码。由任务
compileTestJava
使用。 testRuntimeOnly
扩展runtimeOnly
-
运行测试的运行时依赖项。
testRuntimeClasspath
扩展testRuntimeOnly, testImplementation
-
运行测试的运行时类路径。由任务
test
使用。
以下图表分别显示了主和测试源集的依赖配置。您可以使用此图例来解释颜色
-
绿色背景 - 您可以针对配置声明依赖项。
-
蓝灰色背景 - 配置用于任务使用,而不是您声明依赖项。
-
浅蓝色背景,带等宽文本 - 一个任务。
对于添加到项目的每个源集,Java 插件都会添加以下依赖配置
SourceSet 依赖配置
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
-
Java 工具链,用于使用 JVM 工具的任务,例如编译和执行。默认值:构建 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
-
包含项目的源集。默认值:非空 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 支持两种常见类型的注解处理器的增量编译:“隔离”和“聚合”。请参考以下信息来确定哪种类别适合您的处理器。
您可以通过处理器 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 将始终重新编译处理器生成的任何文件。
流行注解处理器中支持情况
许多流行的注解处理器支持增量注解处理(见下表)。请直接与注解处理器项目联系,获取最新信息和文档。 |
注解处理器 | 支持版本 | 详情 |
---|---|---|
N/A |
||
N/A |
||
部分支持。 |
||
N/A |
||
N/A |
||
DataBinding |
隐藏在功能切换后面 |
|
Dagger |
2.18 功能切换支持,2.24 默认启用 |
|
kapt |
隐藏在功能切换后面 |
|
Toothpick |
N/A |
|
Glide |
N/A |
|
Android-State |
N/A |
|
Parceler |
N/A |
|
Dart 和 Henson |
N/A |
|
N/A |
||
N/A |
||
N/A |
||
Requery |
N/A |
|
N/A |
||
EclipseLink |
N/A |
|
N/A |
||
Immutables |
N/A |
|
2.2.0 功能切换支持,2.3.0-alpha02 默认启用 |
||
N/A |
||
N/A |
||
DBFlow |
N/A |
|
AndServer |
N/A |
|
N/A |
||
N/A |
||
N/A |
||
N/A |
||
隐藏在功能切换后面 |
||
N/A |
编译避免
如果依赖项目以 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 插件都利用 变体感知解析 来处理所使用的依赖项。它们还安装了一组属性兼容性和消除歧义规则,以 配置 Gradle 属性,以适应 JVM 生态系统的具体情况。