Using Gradle for Java Projects

我们已经看到了如何在 Gradle 构建中编写任务以及如何执行它们,但我们还没有看到如何执行现实生活中的任务,例如编译源代码或使用 Gradle 进行测试。

在本章中,我们将讨论如何使用 Gradle Java 插件来获取编译和打包 Java 项目的任务。 我们还将了解 Gradle 的约定构建特性是如何让您轻松的操作源代码。

Why plugins?

在 Gradle 中,我们可以将插件应用到我们的项目中。 插件基本上为我们的项目添加了额外的功能,例如任务【task】和属性【properties】。 通过使用插件,功能与 Gradle 的核心构建逻辑分离。 我们可以编写自己的插件,但 Gradle 还提供了开箱即用的插件。 例如,Gradle 有一个 Java 插件。 这个插件为我们的项目添加了编译、测试和打包 Java 源代码的任务【task】。

与 Gradle 版本打包的插件永远不会针对该版本进行更新或更改,因此如果向插件添加新功能,则会发布全新的 Gradle 版本。 在 Gradle 的未来版本中,这一点将发生变化。 这不适用于我们自己编写的插件。 我们可以发布我们自己的插件的新版本,独立于 Gradle 版本。 让我们先从作为 Gradle 发行版中一部分的 Java 插件开始。

Java 插件入门

Java 插件提供了许多有用的任务【task】和属性【properties】,我们可以使用它们来构建 Java 应用程序或库。 如果我们遵循插件的约定优于配置的支持,我们不必在我们的 Gradle 构建文件中编写大量代码来使用它。 如果我们愿意,我们仍然可以添加额外的配置选项来覆盖插件定义的默认约定。

让我们从一个新的构建文件开始并使用 Java 插件。 我们只需要为我们的构建应用 Java 插件:

apply plugin: 'java' 

就是这样!仅仅通过添加这一行,我们现在就可以在 Java 项目中使用许多任务。要查看插件添加的任务,我们在命令行上运行 tasks 命令并查看输出:

$ gradle tasks
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.
Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]
Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.
Help tasks
----------
components - Displays the components produced by root project 'getting_started'. [incubating]
dependencies - Displays all dependencies declared in root project 'getting_started'.
dependencyInsight - Displays the insight into a specific dependency in root project 'getting_started'.
help - Displays a help message.
model - Displays the configuration model of root project 'getting_started'. [incubating]
projects - Displays the sub-projects of root project 'getting_started'.
properties - Displays the properties of root project 'getting_started'.
tasks - Displays the tasks runnable from root project 'getting_started'.
Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.
Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.
To see all tasks and more detail, run gradle tasks --all
To see more detail about a task, run gradle help --task <task>
BUILD SUCCESSFUL
Total time: 0.849 secs


如果我们查看任务列表,我们可以看到现在可供我们使用的任务数量,这是我们以前没有的; 所有这一切都是通过在我们的构建文件中添加一个简单的行来完成的。

Java 插件添加的任务分布在四个任务组中:

  • 构建任务【Build tasks】:与构建源代码和打包相关的任务
  • 文档任务【Documentation tasks】: javadoc 任务用于生成 Javadoc 文档
  • 验证任务【Verification tasks】:运行测试和检查代码质量的任务
  • 规则任务【Rule】:其中有几个基于规则的任务来构建【build】、上传【upload】和清理【clean】 Java 项目中的工件【artifacts】或任务【task】

除此之外,Java 插件也将所谓的约定【convention】对象添加到我们的项目中。

约定【convention】对象有几个属性和方法,插件中的任务使用这些属性和方法。这些属性和方法被添加到我们的项目中,并且可以像普通项目【project】属性和方法一样被访问。因此,通过使用约定【convention】对象,我们不仅可以查看插件中任务所使用的属性,还可以更改属性的值来重新配置某些任务。

Using the Java plugin

为了使用 Java 插件,我们首先要创建一个非常简单的 Java 源文件。然后我们可以使用插件的任务来构建源文件。您可以根据需要使这个应用程序尽可能复杂,但是为了保持主题清晰,我们将使它尽可能简单。

通过应用Java插件,我们现在必须遵循一些项目目录结构的约定。要构建源代码,我们的 Java 源文件必须位于相对于项目目录的 src/main/java 目录中。如果我们有非 Java 源文件需要包含在 JAR 文件中,我们必须将它们放在 src/main/resources 目录中。我们的测试源文件需要在 src/test/java 目录下,任何测试所需的非 Java 源文件都可以放在 src/test/resources 中。如果我们想要或需要,可以更改这些约定,但最好坚持使用它们,这样我们就不必在构建文件中编写任何额外的代码,这可能会导致错误。

我们将编写的示例 Java 项目,是一个 Java 类,它使用外部属性文件来获取欢迎消息。名为 Sample.java 的源文件位于 src/main/java 目录下,如下:

// File: src/main/java/gradle/sample/Sample.java 
package gradle.sample; 
 
import java.util.ResourceBundle; 
 
/** 
* Read welcome message from external properties file 
* <code>messages.properties</code>. 
*/ 
public class Sample { 
 
    public Sample() { 
    } 
 
    /** 
    * Get <code>messages.properties</code> file 
    * and read the value for <em>welcome</em> key. 
    * 
    * @return Value for <em>welcome</em> key 
    *  from <code>messages.properties</code> 
    */ 
    public String getWelcomeMessage() { 
      final ResourceBundle resourceBundle = ResourceBundle.getBundle("messages"); 
      final String message = resourceBundle.getString("welcome"); 
      return message; 
    } 
} 

在代码中,我们使用 ResourceBundle.getBundle() 来读取我们的欢迎信息。 欢迎消息本身定义在名为 messages.properties 的属性文件中,该文件将位于 src/main/resources 目录中:

# File: src/main/resources/gradle/sample/messages.properties 
welcome = Welcome to Gradle! 

为了编译 Java 源文件并处理属性文件,我们运行 classes 任务。 请注意,Java 插件已添加了 classes 任务。 这就是 Gradle 中所谓的生命周期任务。 classes 任务实际上依赖于另外两个任务——compileJava 和 processResources。 当我们使用 --all 命令行选项运行 tasks 命令时,我们可以看到此任务的依赖关系:

$ gradle tasks --all
...
classes - Assembles main classes.
compileJava - Compiles main Java source.
processResources - Processes main resources.
...

让我们从命令行运行 classes 任务:

$ gradle classes
:compileJava
:processResources
:classes
BUILD SUCCESSFUL
Total time: 1.08 secs

在这里,我们可以看到 compileJava 和 processResources 任务被执行,因为 classes 任务依赖于这些任务。 编译后的类文件和属性文件现在位于 build/classes/main 和 build/resources/main 目录中。 build 目录是 Gradle 用于构建输出文件的默认目录。

如果我们再次执行 classes 任务,我们会注意到这些任务支持 Gradle 的增量构建功能。 由于我们没有更改 Java 源文件或属性文件,并且输出仍然存在,因此可以跳过所有任务,因为它们是最新的:

$ gradle classes
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.595 secs

为了打包我们的类文件和属性文件,我们调用 jar 任务。 这个任务也也是由 Java 插件添加的,并且依赖于 classes 任务。 这意味着如果我们运行 jar 任务,也会执行 classes 任务。 让我们尝试运行 jar 任务,如下:

$ gradle jar
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar
BUILD SUCCESSFUL
Total time: 0.585 secs

生成的 JAR 文件的默认名称是我们项目的名称。 因此,如果我们的项目名为 sample,则 JAR 文件名为 sample.jar。 我们可以在 build/libs 目录中找到该文件。 如果我们查看 JAR 文件的内容,我们会看到已编译的类文件和 messages.properties 文件。 此外,jar 任务会自动添加一个清单文件【manifest file】:

$ jar tvf build/libs/sample.jar
0 Wed Oct 21 15:29:36 CEST 2015 META-INF/
25 Wed Oct 21 15:29:36 CEST 2015 META-INF/MANIFEST.MF
0 Wed Oct 21 15:26:58 CEST 2015 gradle/
0 Wed Oct 21 15:26:58 CEST 2015 gradle/sample/
685 Wed Oct 21 15:26:58 CEST 2015 gradle/sample/Sample.class
90 Wed Oct 21 15:26:58 CEST 2015   gradle/sample/messages.properties

我们还可以执行 assemble 任务来创建 JAR 文件。assemble 任务是另一个生命周期任务,它依赖于 jar 任务,并可以由其他插件进行扩展。我们还可以添加对其他任务的依赖,这些任务为 JAR 文件以外的项目创建包,例如 WAR 文件或 ZIP 存档文件【archive file】:

$ gradle assemble
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:assemble UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.607 secs

要重新开始并清除前面任务生成的所有输出,我们可以使用 clean 任务。此任务删除项目 build 目录和该目录中生成的所有文件。因此,如果我们从命令行执行 clean 任务,Gradle 将会删除 build 目录:

$ gradle clean
:clean
BUILD SUCCESSFUL
Total time: 0.583 secs

注意,Java 插件还添加了一些基于规则的任务。其中一个是 clean<TaskName>。我们可以使用这个任务来删除特定任务的输出文件。clean 任务删除完整的 build 目录;但是使用 clean<TaskName>,我们只删除被命名任务创建的文件和目录。例如,要清理 compileJava 任务生成的 Java 类文件,我们执行 cleanCompileJava 任务。因为这是一个基于规则的任务,所以Gradle 将确定 clean 之后的所有内容都必须是我们项目中有效的任务。由这个任务创建的文件和目录会被 Gradle 确定并删除:

$ gradle cleanCompileJava
:cleanCompileJava UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.578 secs

使用 source sets

Java 插件还向我们的项目添加了一个新概念:source sets。source sets 是一起被编译【compiled】和执行【executed】的源文件的集合。这些文件可以是 Java 源文件【Java source files】或资源文件【 resource files】。在我们的项目中,source sets 可以用于将具有某种意义的文件分组在一起,而不必创建单独的项目。例如,我们可以将描述 Java 项目 API 的源文件的位置分离到一个 source sets 中,并运行只应用于该 source sets 中文件的任务。

在没有任何配置的情况下,我们已经有了两个 source set:main  和 test,它们是由 Java 插件添加的。对于每个 source set ,插件还添加了以下三个任务:compile<SourceSet>Java、process<SourceSet>Resources 和 <SourceSet>Classes。当 source set 被命名为 main 时,在执行任务时不必提供 source set 的名称。例如,compileJava 应用于main source set,而compileTestJava 应用于 test source set。

每个 source set 还有一些属性,可以用来访问组成 source set 的目录和文件。下表显示了我们可以在 source set 中访问的属性:

Source set propertyTypeDescription
javaSourceDirectorySet这些是这个项目的Java源文件。这个集合中只有扩展名为 .java 的文件。
allJavaSourceDirectorySet默认情况下,这与 java 属性相同,因此它包含所有 Java 源文件。其他插件可以向这个集合添加额外的源文件。
resourcesSourceDirectorySet这些是这个 source set 的所有资源文件。它包含资源源目录中的所有文件,不包括扩展名为 .java 的任何文件。
allSourceSourceDirectorySet默认情况下,这是 resources 和 java 属性的组合。这包括该 source set 的所有源文件,包括资源文件和 Java 源文件。
outputSourceSetOutput这些是 source set 中源代码文件的输出文件。它包含已编译的类和已处理的资源。
java.srcDirsSet<File>这些是包含 Java 源代码文件的目录。
resources.srcDirsSet<File>这些目录包含这个 source set 的资源文件。
output.classesDirFile这是输出目录,其中包含这个 source set 中 Java 源代码文件的编译类文件。
output.resourcesDirFile这是输出目录,其中包含来自此 source set 中资源的已处理资源文件。
nameString这是 source set 的只读的名称。

我们可以通过项目的 sourceSets 属性来访问这些属性。在下面的例子中,我们将创建一个新任务来显示几个属性的值:

apply plugin: 'java' 
task sourceSetJavaProperties << { 
    sourceSets { 
        main { 
            println "java.srcDirs = ${java.srcDirs}" 
            println "resources.srcDirs = ${resources.srcDirs}" 
            println "java.files = ${java.files.name}" 
            println "allJava.files = ${allJava.files.name}" 
            println "resources.files = ${resources.files.name}" 
            println "allSource.files = ${allSource.files.name}" 
            println "output.classesDir = ${output.classesDir}" 
            println "output.resourcesDir = ${output.resourcesDir}" 
            println "output.files = ${output.files}" 
      } 
    } 
} 

当我们运行 sourceSetJavaproperties 任务时,我们得到以下输出:

$ gradle sourceSetJavaproperties
:sourceSetJavaProperties
java.srcDirs = [/gradle-book/Chapter4/Code_Files/sourcesets/src/main/java]
resources.srcDirs = [/gradle-book/Chapter4/Code_Files/sourcesets/src/main/resources]
java.files = [Sample.java]
allJava.files = [Sample.java]
resources.files = [messages.properties]
allSource.files = [messages.properties, Sample.java]
output.classesDir = /gradle-book/Chapter4/Code_Files/sourcesets/build/classes/main
output.resourcesDir = /gradle-book/Chapter4/Code_Files/sourcesets/build/resources/main
output.files = [/gradle-book/Chapter4/Code_Files/sourcesets/build/classes/main, /gradle-book/Chapter4/Code_Files/sourcesets/build/resources/main]
BUILD SUCCESSFUL
Total time: 0.594 secs

Creating a new source set

我们可以在项目中自定义一个 source set 。一个 source set 包含所有彼此相关的源文件。在我们的示例中,我们将添加一个新的 source set ,以包含一个 Java 接口。然后,我们的 Sample 类将实现接口;但是,当我们使用单独的 source set  时,我们可以稍后使用它来创建一个单独的 JAR 文件,其中只包含已编译的接口类。我们将该 source set 命名为 api,因为接口实际上是我们示例项目的 API ,我们可以与其他项目共享。

要定义这个 source set ,我们只需要将其名称放在项目的 sourceSets 属性中,如下所示:

apply plugin: 'java' 
 
sourceSets { 
    api 
} 

Gradle将基于这个 source set 创建三个新任务——apiClassescompileApiJava, 和 processApiResources。在执行 tasks 命令后,我们可以看到这些任务:

$ gradle tasks --all
...
Build tasks
-----------
apiClasses - Assembles api classes.
compileApiJava - Compiles api Java source.
processApiResources - Processes api resources.

我们已经在 src/api/java 目录中创建了Java 接口,该目录是 api source set 的 Java 源文件的源目录。下面的代码允许我们看到该 Java 接口:

// File: src/api/java/gradle/sample/ReadWelcomeMessage.java 
package gradle.sample; 
 
/** 
* Read welcome message from source and return value. 
*/ 
public interface ReadWelcomeMessage { 
 
    /** 
    * @return Welcome message 
    */ 
    String getWelcomeMessage(); 
 
} 

要编译源文件,可以执行 compileApiJava 或 apiClasses 任务:

$ gradle apiClasses
:compileApiJava
:processApiResources UP-TO-DATE
:apiClasses
BUILD SUCCESSFUL
Total time: 0.595 secs

源文件编译到 build/classes/api 目录中。

现在,我们将更改 Sample 类的源代码,并实现 ReadWelcomeMessage 接口,如下所示:

// File: src/main/java/gradle/sample/Sample.java 
package gradle.sample; 
 
import java.util.ResourceBundle; 
 
/** 
* Read welcome message from external properties file 
* <code>messages.properties</code>. 
*/ 
public class Sample implements ReadWelcomeMessage { 
 
    public Sample() { 
    } 
 
    /** 
    * Get <code>messages.properties</code> file 
    * and read the value for <em>welcome</em> key. 
    * 
    * @return Value for <em>welcome</em> key 
    *         from <code>messages.properties</code> 
    */ 
    public String getWelcomeMessage() { 
      final ResourceBundle resourceBundle =           ResourceBundle.getBundle("messages"); 
      final String message = resourceBundle.getString("welcome"); 
      return message; 
    } 
 
} 

接下来,我们运行 classes 任务来重新编译我们修改过的 Java 源文件:

$ gradle classes
:compileJava
/gradle-book/Chapter4/src/main/java/gradle/sample/Sample.java:10: error: cannot find symbol
public class Sample implements ReadWelcomeMessage {
                              ^
symbol: class ReadWelcomeMessage
1 error
:compileJava FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':compileJava'.
> Compilation failed; see the compiler error output for details.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 0.608 secs

我们得到一个编译错误! Java 编译器找不到 ReadWelcomeMessage 接口。 但是,我们只是运行了 apiClasses 任务并编译了接口而没有错误。

为了解决这个问题,我们必须定义 classes 和 apiClasses 任务之间的依赖关系。 classes 任务依赖于 apiClasses 任务。 首先,必须编译接口,然后必须编译实现该接口的类。

接下来,我们必须将包含已编译接口的类文件的输出目录,添加到 main source set 的 compileClasspath 属性中。 完成此操作后,我们确定 Java 编译器会拿起已编译的类文件来编译 Sample 类。

为此,我们将更改构建文件,并添加两个任务和 main source set 配置之间的任务依赖关系,如下所示:

apply plugin: 'java' 
n 
sourceSets { 
    api 
    main { 
      compileClasspath += files(api.output.classesDir) 
    } 
} 
 
classes.dependsOn apiClasses 

现在我们可以再次运行 classes 任务,不会出现错误:

$ gradle classes
:compileApiJava
:processApiResources UP-TO-DATE
:apiClasses
:compileJava
:processResources
:classes
BUILD SUCCESSFUL
Total time: 0.648 secs

Custom configuration

如果我们将 Gradle 用于现有项目,我们可能具有与 Gradle 定义的默认结构不同的目录结构,或者我们可能出于其他原因想要具有不同的结构。 我们可以通过配置 source set 并为源目录【source directories】使用不同的值来解决这个问题。

考虑我们有一个具有以下源目录【source directories】结构的项目:

. 
├── resources 
│   ├── java 
│   └── test 
├── src 
│   └── java 
├── test 
│   ├── integration 
│   │   └── java 
│   └── unit 
│       └── java 
└── tree.txt 

我们将需要重新配置 main source set 和 test source set,但我们还必须添加一个新的 integration-test source set。 以下代码反映了source set 的目录结构:

apply plugin: 'java'

sourceSets {
    main {
        java {
            srcDir 'src/java'
        }
        resources {
            srcDir 'resources/java'
        }
    }
    test {
        java {
            srcDir 'test/unit/java'
        }
        resources {
            srcDir 'resources/test'
        }
    }

    'integeration-test' {
        java {
            srcDir 'test/integration/java'
        }
        resources {
            srcDir 'resources/test'
        }
    }
}

注意我们必须将 integration-test source set 的名称放在引号中; 这是因为我们在名称中使用了连字符。 然后,Gradle 会将 source set 的名称转换为 integrationTest(不带连字符并带有大写 T)。 例如,要编译 integration-test source set 的源文件,我们使用 compileIntegrationTestJava 任务。

使用 properties

我们已经讨论过 Java 插件将任务【tasks】 和 source sets 添加到我们的 Gradle 项目中;但是,我们也获得了许多可以使用的新属性。插件的自定义属性可以在 org.gradle.api.plugins.Convention 类型的 Convention 对象中设置。插件使用 Convention 对象来公开我们可以在项目中使用的属性和方法。插件的 Convention 对象被添加到项目【project】的 convention 属性中。 Gradle 项目的 convention 属性是插件中所有 Convention 对象的容器。

我们可以直接从插件的 Convention 对象中作为项目属性直接访问,或者我们可以指定到插件的 Convention 对象的完整路径,以便获取属性或调用方法。

例如,sourceSets 属性是 Java 插件的 Convention 对象的属性。通过以下任务 showConvention,我们可以看到访问此属性的不同方式:

task showConvention << { 
    println sourceSets.main.name 
    println project.sourceSets.main.name 
    println project.convention.plugins.java.sourceSets.main.name 
} 

要查看我们可用的所有属性,我们必须从命令行调用 properties 任务。以下输出显示了 properties 任务的部分输出:

$ gradle properties
:properties
...
targetCompatibility: 1.8
tasks: [task ':buildDependents', task ':buildNeeded', task ':check', task ':classes', task ':compileJava', task ':compileTestJava', task ':jar', task ':javadoc', task ':processResources', task ':processTestResources', task ':properties', task ':test', task ':testClasses']
test: task ':test'
testClasses: task ':testClasses'
testReportDir: /gradle-book/Chapter4/Code_Files/props/build/reports/tests
testReportDirName: tests
testResultsDir: /gradle-book/Chapter4/Code_Files/props/build/test-results
testResultsDirName: test-results
version: unspecified
...

如果我们查看该清单,我们会看到很多属性,我们可以使用这些属性重新定义存储编译或测试任务的输出文件的目录。下表显示了目录属性:

属性名默认值描述
distDirNamedistributions
这是一个相对于构建目录的目录名称,用于存储分发文件【distribution files】
libsDirNamelibs这是存储生成的 JAR 文件的目录名;它是相对于构建目录的
dependencyCacheDirNamedependency-cache
这是用于存储有关依赖项的缓存信息的目录的名称;它是相对于构建目录的
docsDirNamedocs这是存储生成文档的目录名称;它是相对于构建目录的
testReportDirNametests
这是相对于构建目录的目录名称,用于存储测试报告
testResultsDirNametest-results这存储了测试结果 XML 文件;它是相对于构建目录的

Java插件还向我们的项目添加了其他属性。这些属性可用于设置编译 Java 源文件的 Java 版本的源和目标兼容性,或为生成的 JAR 文件设置基本文件名。

下表显示了 Java 插件的约定属性:

属性名类型默认值描述
archivesBaseNameStringrce and target compatibility of the Java version for compili项目名这是用于存档任务(如JAR)创建的存档的基本文件名
sourceCompatibilityStringNumberJavaVersionObject用于Gradle的Java版本这是使用编译任务编译Java源文件时要使用的Java版本兼容性
targetCompatibilityString,Number,JavaVersion,Object等同于sourceCompatibility这是生成Java类文件的版本
sourceSetsSourceSetContainer-这些是项目的 source set
manifestManifestEmpty manifest这是要包含在所有JAR文件中的清单【manifest】
metaInfListEmpty list这是项目中创建的所有 JAR 文件的 META-INF 目录中要包含的文件列表

在我们的示例项目中,我们已经看到生成的 JAR 文件是以项目名称命名的;但是通过 archivesBaseName 属性,我们可以改变它。我们还可以将我们的项目的源代码兼容性改为Java 6。最后,我们还可以更改用于生成的 JAR 文件的清单【manifest】。下面的构建文件反映了所有的变化:

apply plugin: 'java' 
 
archivesBaseName = 'gradle-sample' 
version = '1.0' 
 
sourceCompatibility = JavaVersion.VERSION_1_8 // Or '1.8' or 8 
 
jar { 
    manifest { 
      attributes( 
        'Implementation-Version' : version, 
        'Implementation-Title' : 'Gradle Sample' 
      ) 
    } 
} 
...

现在,如果我们调用 assemble 任务来创建 JAR 文件,并查看 build/libs 目录,我们可以看到JAR文件现在名为 gradle-sample-1.0.jar:

$ gradle assemble
:compileApiJava
:processApiResources UP-TO-DATE
:apiClasses
:compileJava
:processResources
:classes
:jar
:assemble
BUILD SUCCESSFUL
Total time: 0.657 secs
$ ls build/libs
gradle-sample-1.0.jar

要查看生成的 manifest 文件的内容,我们将首先从 JAR 文件中提取该文件,然后查看内容:

$ cd build/libs
$ jar xvf gradle-sample-1.0.jar
created: META-INF/
inflated: META-INF/MANIFEST.MF
created: gradle/
created: gradle/sample/
inflated: gradle/sample/Sample.class
inflated: gradle/sample/messages.properties
$ cat META-INF/MANIFEST.MF
Manifest-Version: 1.0
Implementation-Version: 1.0
Implementation-Title: Gradle Sample

创建 Javadoc 文档

要生成 Javadoc 文档,必须使用 org.gradle.api.tasks.javadoc.Javadoc 类型的 Javadoc 任务。该任务为 main source set 中的 Java 源文件生成文档。如果我们想在我们的项目中为 sources set 生成文档,我们必须配置 javadoc 任务或向我们的项目添加一个额外的 javadoc 任务。

请注意,在我们的项目中,我们有一个 API source set 和一个带有 Java 源文件的 main source set。如果我们想为两个 source set 生成文档,我们必须在项目中配置 javadoc 任务。javadoc 任务的 source 属性默认设置为 sourceSets.main.allJava。如果我们将 sourceSets.api.allJava 添加到 source 属性中,我们的接口文件也会被 javadoc 任务处理:

apply plugin: 'java' 
 
javadoc { 
    source sourceSets.api.allJava 
} 
...

接下来,我们可以运行 javadoc 任务,生成文档并放入 build/docs/javadoc 目录:

$ gradle javadoc
:compileApiJava
:processApiResources UP-TO-DATE
:apiClasses
:compileJava
:processResources
:classes
:javadoc
BUILD SUCCESSFUL
Total time: 1.503 secs

我们可以在 javadoc 任务中设置更多属性。例如,我们可以使用 title 属性为生成的文档设置标题。默认值是项目名称后加上项目版本号(如果可用的话)。

要更改目标目录【destination directory】,我们可以将 javadoc 任务的 destinationDir 属性设置为我们想要的目录。

我们还可以使用 options 属性来定义 Java SDK javadoc工具中知道的许多属性。下面的例子向我们展示了如何在我们的项目中为 javadoc 任务设置一些选项:

apply plugin: 'java' 
 
javadoc { 
    source sourceSets.api.allJava 
 
    title = 'Gradle Sample Project' 
 
    options.links = ['http://docs.oracle.com/javase/6/docs/api/'] 
    options.footer = "Generated on ${new Date().format('dd MMM yyyy')}" 
    options.header = "Documention for version ${project.version}" 
} 
... 

Assembling archives

如果我们希望将新的 API source set 的输出打包到 JAR 文件中,我们必须自己定义一个新任务。Gradle 并没有为我们自动提供一些神奇的功能;幸运的是,任务本身非常简单:
 

apply plugin: 'java' 
 
archivesBaseName = 'gradle-sample' 
version = '1.0' 
 
sourceSets { 
    api 
    main { 
        compileClasspath += files(api.output.classesDir) 
    } 
} 
 
classes.dependsOn apiClasses 
 
task apiJar(type: Jar) { 
    // Define appendix that will be 
    // appended to the archivesBaseName 
    // for the JAR. 
    appendix = 'api' 
 
    // Define the input for the JAR file. 
    from sourceSets.api.output 
} 

apiJar 任务是一个 Jar 任务。我们定义用于生成 JAR 文件最终文件名的 appendix【附录】属性。我们使用 from() 方法指向 API source set 的输出目录,因此所有生成的输出都包含在JAR文件中。当我们运行 apiJar 任务时,会在 build/libs 目录下生成一个新的gradle-sample-api-1.0.jar JAR文件,如下所示:

$ gradle apiJar
:compileApiJava
:processApiResources UP-TO-DATE
:apiClasses
:apiJar
BUILD SUCCESSFUL
Total time: 3.242 secs

JAR 文件的基名【base name 】是项目名,与 jar 任务的项目名类似。如果我们看一下内容,我们会看到编译好的 ReadWelcomeMessage 类文件:

$ jar tvf build/libs/sample-api.jar
  0 Thu Oct 22 16:38:56 CEST 2015 META-INF/
25 Thu Oct 22 16:38:56 CEST 2015 META-INF/MANIFEST.MF
  0 Thu Oct 22 16:35:08 CEST 2015 gradle/
  0 Thu Oct 22 16:35:08 CEST 2015 gradle/sample/
182 Thu Oct 22 16:38:56 CEST 2015 gradle/sample/ReadWelcomeMessage.class

还需要注意的是,我们没有定义 apiJar 和 apiClasses 任务之间的任务依赖关系;但是当我们运行apiJar 任务时,Gradle 会自动运行 apiClasses 任务。这是因为我们使用了sourceSets.api.output属性来定义 JAR 文件中需要包含的文件;Gradle 注意到了这一点,并确定了负责在sourceSets.api.output 目录中创建内容的任务。apiClasses 任务是编译 Java 源文件并将资源文件处理到 build 目录的任务,因此,Gradle 会在 apiJar 任务之前首先调用 apiClasses 任务。

Summary

在本章中,我们讨论了 Gradle 对 Java 项目的支持。通过应用 Java 插件所需的简单代码行,我们获得了大量可以用于 Java 代码的功能。我们可以编译源文件,将编译后的代码打包到 JAR 文件中,并生成文档。

在下一章中,我们将看到如何向外部库【external libraries】添加依赖项。我们还将讨论如何配置存储库,并通过配置来组织我们的依赖关系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值