vlambda博客
学习文章列表

读书笔记《gradle-essentials》与Gradle合作的真实世界项目

Chapter 6. The Real-world Project with Gradle

到目前为止,我们已经讨论了构建 Java 项目、Web 项目、Gradle 生命周期以及 Gradle 的多模块特性。众所周知,在 Gradle 之前,市场上还有很多其他的构建工具,其中最流行的是 Ant 和 Maven。由于这两个构建工具中已经编写了许多项目构建脚本。在本章中,我们将讨论将项目现有构建脚本从 Ant、Maven 迁移到 Gradle 的不同迁移策略。除此之外,我们还将专注于将 Gradle 构建脚本集成到 Jenkins 等持续集成工具中,并为代码生成 Java 文档。

Migrating from an Ant-based project


Ant 是最初和最流行的构建工具之一,与其他基于脚本的原生构建工具相比,它使构建和部署过程更加简单。不过,您仍然可以找到许多使用 Ant 构建脚本来构建项目的项目。 Ant 是根据命令式编程模型的哲学开发的,它告诉系统要做什么以及如何去做。因此,您可以控制构建脚本的每一个动作或步骤。以下是构建任何 Java 项目的样例 Ant 构建脚本。在这里,我们只考虑构建 Java 项目所需的最少任务,因为我们的目的是讨论从 Ant 脚本迁移到 Gradle 脚本的策略:

<project name="Ant build project" default="createJar">
  <target name="clean" description="clean the existing dirs">
    <delete dir="build"/>
    <delete dir="dist"/>
  </target>

  <target name="compile" description="compile the source" depends="clean">
    <mkdir dir="build"/>
    <mkdir dir="dist"/>
    <mkdir dir="build/classes"/>
    <javac srcdir="src" destdir="build/classes"/>
  </target>
  <target name="createJar" depends="compile" description="create the jar">
    <jar jarfile="dist/JavaProject-1.0.jar" basedir="build/classes"/>
  </target>
</project>

在这里,我们定义了cleancompile<三个target /code> 和 createJar,它将删除目录,创建目录,编译源目录中的 Java 文件,最后创建 .jar 文件,分别。为了将构建脚本从 Ant 迁移到 Gradle,开发人员可以遵循三种不同的策略,如下所示:

  • 导入 Ant 文件

  • 使用 AntBuilder API

  • 将 Ant 任务重写为 Gradle 任务

我们将通过一个例子来讨论它们中的每一个。

Importing an Ant file

第一种也是最简单的迁移方法是将 Ant 脚本文件直接导入 Gradle 脚本。考虑以下结构:

C:\GRADLE\CHAPTER6
│   build_import.gradle
│   build.xml
│
└───src
    └───main
        └───java
            └───ch6
                    SampleJava.java

这里项目名称为Chapter6,Java源码目录为src/main/java,Ant构建脚本文件为build.xmlbuild.xml 的源代码在上面提到过。现在,作为迁移的一部分,使用以下内容创建 build_import.gradle 文件:

ant.importBuild 'build.xml'

就这些。是的,我们已经成功地将 Ant 构建脚本迁移到 Gradle 脚本。现在,尝试执行以下命令:

> gradle –b build_import.gradle createJar
:clean
:compile
:createJar
BUILD SUCCESSFUL
Total time: 3.045 secs

执行这个后,可以找到build/classes dist目录,dist包含JavaProject.jar< /代码>文件。

Using AntBuilder API

另一种迁移方法是使用AntBuilder API。默认情况下,Gradle 向用户提供了一个 AntBuilder 对象 ant。用户可以直接在 Gradle 脚本中使用该对象来调用 Ant 任务。以下是使用 AntBuilder API 的 build_antbuilder.gradle 文件的示例代码:

task cleanDir << {
  ant.delete(dir:"build")
  ant.delete(dir:"dist")
}

task compileSrc(dependsOn:'cleanDir') << {
  ant.mkdir(dir:"build/classes")
  ant.mkdir(dir:"dist")
  ant.javac(srcdir:"src", destdir:"build/classes", includeantruntime:"false")
}
task createJar(dependsOn:'compileSrc') << {
  ant.jar(destfile: "dist/JavaProject-1.0.jar", basedir:"build/classes")
}

在这里,您可以看到我们使用了不同的 Ant 任务,例如 mkdirjavacjar 等等,作为 ant 对象的方法。现在,执行以下命令:

> gradle –b build_antbuilder.gradle createJar
:cleanDir
:compileSrc
:createJar
BUILD SUCCESSFUL
Total time: 3.437 secs

同样在这里,您 会发现相同的输出,也就是说,它将创建 build/ classes 目录中找到类文件,在 dist 目录中您可以找到 。 jar 文件。

Rewriting Ant tasks to Gradle tasks

是最后的方法。使用这种方法而不是使用 ant 对象,您实际上是使用实际的 Gradle 任务重写了完整的构建逻辑或功能。遵循这种策略的一个简单方法是,用户首先需要在逻辑上理解用 Ant 编写的完整流文件,然后将其逐步转换为 Gradle 脚本。对于 Ant 中定义的所有目标,用户可以在 Gradle 中创建任务,对于 Ant 中定义的所有任务,用户可以使用 Gradle 特性来复制相同的行为。 Gradle 提供了不同的标准插件来支持构建需求的大部分步骤。插件有自己的生命周期,在插件的帮助下,用户可以避免为通用构建功能重写大量样板脚本。 java 插件就是一个这样的插件。我们已经在Chapter 2中看到了java插件细节,构建 Java 项目。如果我们想将此 Ant 脚本迁移到 Gradle 脚本以构建 Java 项目,用户只需使用 Java 插件即可完成工作。

考虑具有以下内容的 build.gradle 文件:

apply plugin:'java'

如果开发人员遵循 java 插件的默认约定,他只需要编写这一行来构建 Java 项目,并在执行 gradle build 命令,所有必需的步骤都将完成,例如编译代码、执行单元测试用例和准备 .jar 文件。但是,情况并非总是如此。许多遗留项目不遵循约定,它们可能有自己的约定。 gradle 插件提供了根据项目需要配置插件的灵活性。我们将在下面的示例代码中将 Ant 脚本重写为 Gradle 脚本:

apply plugin:'java'

task cleanDir << {
  delete "build"
  delete "dist"
}

task createDirs(dependsOn:'cleanDir') << {
  def classes = file("build/classes")
  def dist = file("dist")
  classes.mkdirs()
  dist.mkdirs()
  
}
compileJava {
  File classesDir = file("build/classes")
  FileTree srcDir = fileTree(dir: "src")
  source srcDir
  destinationDir classesDir
}
task createJar(type: Jar) {
  destinationDir = file("dist")
  baseName = "JavaProject-1.0"
  from "build/classes"
}
createJar.dependsOn compileJava
compileJava.dependsOn createDirs

前面的代码片段展示了如何将 Ant 脚本重写为 Gradle 脚本。在执行 gradle createJar 命令时,它会生成与上述迁移策略相同的输出。

Migrating from a Maven project


Maven,另一个构建工具中的,在Ant之后最流行,它还附带了依赖管理解决方案,解决了用户在Ant中面临的问题. Ant 的第一个问题是命令式编程,用户必须编写大量样板代码。另一个问题是依赖管理。 Ant 没有任何内置的依赖管理解决方案(Ant 后来与 Ivy 集成用于依赖管理)。用户必须在需要下载的构建文件中写入每个 JAR 文件路径,并且在传递依赖的情况下,用户识别每个依赖 JAR 并在构建中提及 JAR 名称太复杂了文件。此外,在版本冲突的情况下,它会消耗开发人员的大量精力。 Maven 带有声明式编程模型和内置的依赖管理解决方案。 Gradle 也是建立在这些原则之上的;因此,从 Maven 迁移到 Gradle 对用户来说似乎很舒服。

与 Ant 迁移一样,Gradle 不提供任何导入功能或内置 Maven 对象。用户需要将 Maven 脚本重写为 Gradle 脚本。以下是一些有助于您顺利从 Maven 迁移到 Gradle 的概念:

  • 插件声明

  • 共同约定

  • 依赖管理

  • 存储库配置

让我们继续解释这些概念:

  • 插件声明:插件 是 Maven 和 Gradle 的关键驱动程序功能。与 Maven 插件一样,Gradle 也将其大部分功能打包到插件中。在 Maven 中,用户包含以下 XML 格式的插件:

    <plugin>
      <artifactId>pluginName</artifactId>
      <version>2.3.2</version>
    </plugin>

    要包含插件,用户只需要编写 apply plugin 语句,如下所示:

    apply plugin: '<plugin name>'
  • 通用约定:在 Maven 和 Gradle 中,总是附带一个插件其功能的一些常见约定。例如,如果用户包含 java 插件,通常的约定是源代码位置应该是 src/main/java,测试代码位置应该是src/test/java,以此类推。如果用户包含插件并遵循相同的约定,那么他可以避免编写任何可以节省时间和精力的样板代码。

  • 依赖管理Maven 和 Gradle 都内置了依赖管理特征。用户不需要关心项目所需的每个单独的 JAR。他只需要提到项目中的一级依赖,其余的都由构建工具来处理。

    在 Maven 中,用户可以按以下格式提及依赖项:

    <dependency>
      <groupId> org.apache.logging.log4j</ groupId>
      <artifactId>log4j-core </ artifactId>
      <version>1.2</version>
      <scope>compile</scope>
    </dependency>

    要在 Gradle 中定义依赖项,用户必须使用以下语法:

    dependencies{
    compile(' org.apache.logging.log4j: log4j-core:1.2') 
    }

    什么范围是Maven,依赖配置是Gradle。您可能已经观察到 Maven 中的 scope 属性和 Gradle 中的依赖 配置属性。在 Maven 中,范围标识需要下载构建依赖项的哪个阶段。在 Gradle 中,依赖配置满足了同样的需求。

  • Repositories 配置:每当我们谈到依赖时,首先出现的就是想到的是存储库。这是您下载依赖项的位置。以下是可以帮助您在 Maven 中提及存储库位置的代码片段:

    <repositories>
      <repository>
        <id>repository_1</id>
        <name>custom Name</name>
        <url> http://companylocalrepository.org </url>
       </repository>
    </repositories>

    在 Gradle 中,您可以使用以下语法提及存储库:

    repositories {
      maven {
        url "http://companylocalrepository.org"
      }
    }

正如我们所见,Maven 和 Gradle 在构建任何项目时都遵循相同的理念。主要区别在于 Maven 使用擅长结构的 XML,但在配置构建脚本时可能会很痛苦,而 Gradle 使用作为 DSL 的 Groovy 脚本,在管理和更改默认行为时提供了极大的灵活性。

Publishing artifacts


构建软件没有多大意义,除非您将软件发布到一些公共存储库,以便在需要时可以被其他软件或项目重用。我们在下载依赖项时讨论了存储库。存储库的另一个方面是将构建结果(JAR、WAR、EAR 等)上传到某个公共位置,以便其他开发人员可以下载它。 Gradle 中的不同插件提供了一种自动化方式来发布插件的默认工件。例如,java 插件提供上传 JAR 文件的任务,war 插件提供上传 WAR 文件的任务, scala 插件提供上传 JAR 文件的任务,等等。用户只需要配置 上传存储库 位置。如果用户不想上传默认构建工件或用户想要上传一些自定义工件,他可以轻松自定义 Gradle 任务以上传其他工件以及根据他的自定义要求。

正如我们看到的,一个java插件提供了不同的配置,比如compile、testCompile、runtime等等on,下载特定范围的 JAR。为了上传工件,Gradle 提供了一种额外的配置, archives。用户可以在档案配置中配置工件,并使用 uploadArchive 任务,他可以将工件上传到存储库。

以下是构建文件(build_uploadArtifact.gradle)的示例示例,用于上传由 java 插件生成的 JAR 文件:

apply plugin: 'java'
version=1.0
repositories {
  mavenCentral()
}
dependencies {
  compile ('log4j:log4j:1.2.16')
}
uploadArchives {
  repositories {
    maven {
      credentials {
        username "user1"
        password "user1"
      }
      url "http://company.private.repo"
    }
  }
}

您可以执行 gradle –b build_uploadArtifact.gradle uploadArchives 命令上传工件。作为生命周期的一部分,它将构建和上传工件。

在前面的示例中,uploadArchives 任务将工件上传到存储库(在 URL 中提到)。如果它是安全存储库,您可以提供用户名和密码,或者忽略它。您注意到我们在这里没有提到档案,那么 会被上传吗?正如我们已经讨论过的,java 插件构建 JAR 文件,war 插件构建 WAR 文件,等等。因此,插件生成的默认工件将默认作为 uploadArchives 任务的一部分上传。我们将看到另一个关于如何上传您的自定义工件的示例。

以下是 build_uploadCustom.gradle 文件:

apply plugin: 'java'
archivesBaseName="JavaProject" // to customize Jar Name
version=1.0
repositories {
  mavenCentral()
}
def customFile= file('configurations.xml')
task customZip(type: Zip) {
  from 'src'
}
artifacts {
  archives customFile
  archives customZip
}
uploadArchives {
  repositories {
    flatDir {dirs "./tempRepo"}
  }
}

现在,执行 gradle –b build_uploadCustom.gradle uploadArchives 命令:

>gradle -b build_uploadCustom.gradle uploadArchives
:customZip UP-TO-DATE
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:uploadArchives

BUILD SUCCESSFUL

Total time: 4.014 secs

在这里,您可以发现执行构建脚本后,创建了一个新目录tempRepo。这包含由 Gradle 脚本发布的所有上述工件(ZIP、JAR 和 XML 文件)。

在前面的示例中,我们介绍了以下两种情况:

  • 上传自定义文件(XML 和 ZIP 文件以及默认工件)

  • 上传到本地文件系统(不在中央存储库上)

如果您将任何其他自定义文件(JAR、WAR 或任何其他文件)配置到档案,它也会被上传到存储库。在这里,我们配置了两个附加文件,一个 .xml 文件和一个 .zip 文件以及默认的 Java 工件。如果您想与您的团队成员共享您的工件,同时又不想将工件上传到存储库,除非它通过集成测试,Gradle 让您可以使用 flatDir

Gradle 最近引入了一个 maven-publish 插件来更好地控制发布过程。它为您提供了许多额外的灵活性以及默认的发布任务。用户可以修改 POM 文件、发布多个模块等等。

Continuous Integration


持续集成CI)是你可以随处阅读的最流行的流行语之一。顾名思义,CI就是每次集成代码库的过程;每当有人对存储库进行提交时。它编译代码,运行单元测试用例并准备构建。用户在这里获得的好处之一是,如果存在编译问题和集成问题,用户可以在早期阶段弄清楚,而不是为时已晚。以下是 CI 工具遵循的通用工作流程:

读书笔记《gradle-essentials》与Gradle合作的真实世界项目

图 6.1

Gradle 如何适应这个流程?要为任何软件规划构建和部署自动化解决方案,我们需要一组不同的工具来协同工作以实现共同目标。 Jenkins 是有助于集成完整工作流程的集成工具之一。它也适用于插件的概念;您可以根据需要向 Jenkins 添加不同的插件(例如 Gradle、Git、Svn 等),并对其进行配置以规划自动化流程。

在这里,我们假设您已经安装了 Jenkins。您可以通过导航到 Manage Jenkins | 来安装 Gradle 插件 | 管理插件 | 搜索 Gradle

读书笔记《gradle-essentials》与Gradle合作的真实世界项目

图 6.2

安装插件后,您可以使用以下屏幕截图在 Jenkins 中配置作业:

读书笔记《gradle-essentials》与Gradle合作的真实世界项目

图 6.3

项目配置界面下,您需要配置存储库路径。默认情况下,Jenkins 提供 CVS 和 SVN 插件。如果您需要任何其他存储库(perforce 或 Git),您可以添加相应的插件。仓库配置完成后,需要配置Build Triggers。它允许您定期触发构建,或者,如果您想在每次提交时构建,您可以选择 Poll SCM。现在,是时候配置将构建项目的构建脚本了。

Build 菜单下,您可以选择 Invoke Gradle script

读书笔记《gradle-essentials》与Gradle合作的真实世界项目

图 6.4

如果你是使用默认构建文件名build.gradle,则无需配置构建文件。在 Task 下,您可以提及要执行的任务的名称。例如,如果要构建项目,可以在文本框中提及 build

配置完成后,您可以点击左侧菜单中的Build Now来构建项目。完成后,单击相应的内部版本号,它将在主屏幕上显示 Console Output

读书笔记《gradle-essentials》与Gradle合作的真实世界项目

图 6.5

Generating documentation


文档开发生命周期的重要组成部分之一,没有得到开发者的足够重视。如果代码没有正确记录,它总是会增加维护工作,如果代码缺少文档,新团队成员也需要时间来理解代码。当您将 Java 插件 应用到构建文件时,Gradle 会为您提供一个 javadoc 任务。默认情况下,Gradle 会为您的代码生成初始文档,即使用户没有在文件中提及任何 Javadoc

考虑以下 Java 示例代码:

package ch6;
public class SampleTask  {
  public static void main(String[] args) {
    System.out.println("Building Project");

  }
  public String greetings(String name) {
    return "hello "+name;
  }
}

现在,尝试执行以下命令:

> gradle clean javadoc
:clean
:cleanDir
:createDirs
:compileJava
:processResources UP-TO-DATE
:classes
:javadoc

BUILD SUCCESSFUL

Total time: 4.341 secs

此命令 将在 <project> 处生成基本的 Java 文档。 \build\docs\javadoc

根据要求,您可以在上面添加自己的标签(@description@param 等)和详细信息类并获取更新的 Java 文档。

Summary


在本章中,我们讨论了从现有构建工具到 Gradle 的不同迁移策略,这对于计划将现有 Ant 和 Maven 脚本迁移到 Gradle 的用户来说非常方便。我们还讨论了如何将工件发布到存储库,这是任何构建工具的关键功能,它可以帮助用户始终从存储库中获取最新的工件。我们在 Jenkins 的帮助下讨论了 CI 框架,以及 Gradle 如何适应这个流程,同时自动化构建和部署解决方案。最后,我们讨论了如何为 Java 代码生成文档。

在下一章中,我们将讨论如何将 TestNG 与 Gradle 集成,这将帮助用户将测试用例作为 Gradle 构建的一部分运行。我们还将讨论集成测试策略以及 Gradle 与代码分析和代码覆盖工具的集成。