Contents

Ant、Maven 和 Gradle

1. 简介

在本文中,我们将探讨主导 JVM 生态系统的三个 Java 构建自动化工具——Ant、Maven 和 Gradle

我们将介绍它们中的每一个,并探索 Java 构建自动化工具是如何演变的。

2. Apache Ant

**一开始,Make 是唯一一款超越本土解决方案的构建自动化工具。**Make 自 1976 年以来一直存在,因此,它在 Java 早期用于构建 Java 应用程序。

但是,C 程序的许多约定并不适合 Java 生态系统,因此 Ant 及时取代了它作为更好的选择。

Apache Ant (“Another Neat Tool”)是一个 Java 库,用于自动化 Java 应用程序的构建过程。此外,Ant 可用于构建非 Java 应用程序。它最初是 Apache Tomcat 代码库的一部分,并于 2000 年作为独立项目发布。

在许多方面,Ant 与 Make 非常相似,而且它非常简单,因此任何人都可以在没有任何特定先决条件的情况下开始使用它。Ant 构建文件是用 XML 编写的,按照惯例,它们被称为build.xml

构建过程的不同阶段称为“目标”。

下面是一个带有HelloWorld主类的简单 Java 项目的build.xml文件示例:

<project>
    <target name="clean">
        <delete dir="classes" />
    </target>
    <target name="compile" depends="clean">
        <mkdir dir="classes" />
        <javac srcdir="src" destdir="classes" />
    </target>
    <target name="jar" depends="compile">
        <mkdir dir="jar" />
        <jar destfile="jar/HelloWorld.jar" basedir="classes">
            <manifest>
                <attribute name="Main-Class" 
                  value="antExample.HelloWorld" />
            </manifest>
        </jar>
    </target>
    <target name="run" depends="jar">
        <java jar="jar/HelloWorld.jar" fork="true" />
    </target>
</project>

该构建文件定义了四个目标:cleancompilejarrun。例如,我们可以通过运行来编译代码:

ant compile

这将首先触发目标clean,这将删除“类”目录。之后,目标compile将重新创建目录并将 src 文件夹编译到其中。

**Ant 的主要优点是它的灵活性。Ant 没有强加任何编码约定或项目结构。**因此,这意味着 Ant 需要开发人员自己编写所有命令,这有时会导致难以维护的巨大 XML 构建文件。

由于没有约定,只知道 Ant 并不意味着我们会很快理解任何 Ant 构建文件。习惯一个不熟悉的 Ant 文件可能需要一些时间,与其他较新的工具相比,这是一个劣势。

起初,Ant 没有对依赖管理的内置支持。然而,随着依赖管理在后来的几年中成为必须,Apache Ivy 被开发为 Apache Ant 项目的子项目。它与 Apache Ant 集成,并且遵循相同的设计原则。

然而,最初的 Ant 限制是由于没有对依赖管理的内置支持以及在处理不可管理的 XML 构建文件时的挫败感,这导致了 Maven 的创建。

3. Apache Maven

Apache Maven 是一个依赖管理和构建自动化工具,主要用于 Java 应用程序。**Maven 继续像 Ant 一样使用 XML 文件,但以一种更易于管理的方式。**这里的游戏名称是约定优于配置。

Ant 提供了灵活性并要求一切从头开始编写,而Maven 依赖于约定并提供预定义的命令(目标)。

简而言之,Maven 允许我们专注于构建应该做什么,并为我们提供了框架来完成它。Maven 的另一个积极方面是它为依赖管理提供了内置支持。

Maven 的配置文件,包含构建和依赖管理指令,按照约定称为pom.xml。此外,Maven 还规定了严格的项目结构,而 Ant 也提供了灵活性。

下面是一个pom.xml文件示例,用于与之前的HelloWorld主类相同的简单 Java 项目:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
      http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>blogdemo</groupId>
    <artifactId>mavenExample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <description>Maven example</description>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

但是,现在项目结构也已经标准化,并且符合 Maven 约定:

+---src
|   +---main
|   |   +---java
|   |   |   \---com
|   |   |       \---blogdemo
|   |   |           \---maven
|   |   |                   HelloWorld.java
|   |   |                   
|   |   \---resources
|   \---test
|       +---java
|       \---resources

与 Ant 相比,无需手动定义构建过程中的每个阶段。相反,我们可以简单地调用 Maven 的内置命令。

例如,我们可以通过运行来编译代码:

mvn compile

正如官方页面所指出的,**Maven 的核心可以被认为是一个插件执行框架,因为所有工作都是由插件完成的。**Maven 支持范围广泛的可用插件 ,每个插件都可以额外配置。

可用的插件之一是 Apache Maven 依赖插件,它有一个copy-dependencies目标,它将我们的依赖复制到指定的目录。

为了展示这个插件,让我们在pom.xml文件中包含这个插件,并为我们的依赖项配置一个输出目录:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/dependencies
                          </outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

这个插件将在package阶段执行,所以如果我们运行:

mvn package

我们将执行此插件并将依赖项复制到 target/dependencies 文件夹。

还有一篇 关于如何使用不同的 Maven 插件创建可执行 JAR 的文章。此外,有关 Maven 的详细概述,请查看有关 Maven 的核心指南 ,其中探讨了 Maven 的一些关键特性。

Maven 变得非常流行,因为构建文件现在已经标准化,并且与 Ant 相比,维护构建文件所需的时间明显减少。然而,尽管比 Ant 文件更标准化,Maven 配置文件仍然趋于变得庞大和繁琐。

**Maven 的严格约定的代价是灵活性不如 Ant。**目标定制非常困难,因此与 Ant 相比,编写定制构建脚本要困难得多。

尽管 Maven 在使应用程序的构建过程更容易和更标准化方面做出了一些重大改进,但由于比 Ant 灵活得多,它仍然要付出代价。这导致了 Gradle 的创建,它结合了两全其美——Ant 的灵活性和 Maven 的特性。

4. Gradle

Gradle 是一个依赖管理和构建自动化工具,它建立在 Ant 和 Maven 的概念之上。

关于 Gradle,我们首先要注意的一点是它不使用 XML 文件,这与 Ant 或 Maven 不同。

随着时间的推移,开发人员对拥有和使用特定领域的语言越来越感兴趣——简单地说,这将允许他们使用为特定领域量身定制的语言来解决特定领域中的问题。

这被 Gradle 采用,它使用基于GroovyKotlin 的 DSL 。**由于该语言是专门为解决特定领域问题而设计的,因此这导致了更小的配置文件和更少的混乱。**Gradle 的配置文件按照惯例在 Groovy 中称为build.gradle ,在 Kotlin中称为build.gradle.kts

请注意,Kotlin 在自动完成和错误检测方面提供了比 Groovy 更好的 IDE 支持。

下面是一个build.gradle文件的示例,用于与之前的HelloWorld主类相同的简单 Java 项目:

apply plugin: 'java'
repositories {
    mavenCentral()
}
jar {
    baseName = 'gradleExample'
    version = '0.0.1-SNAPSHOT'
}
dependencies {
    testImplementation 'junit:junit:4.12'
}

我们可以通过运行来编译代码:

gradle classes

Gradle 的核心是有意提供很少的功能。**插件添加了所有有用的功能。**在我们的示例中,我们使用了java插件,它允许我们编译 Java 代码和其他有价值的功能。

**Gradle 将其构建步骤命名为“任务”,而不是 Ant 的“目标”或 Maven 的“阶段”。**在 Maven 中,我们使用了 Apache Maven Dependency Plugin,将依赖项复制到指定目录是一个特定的目标。使用 Gradle,我们可以使用任务来做同样的事情:

task copyDependencies(type: Copy) {
   from configurations.compile
   into 'dependencies'
}

我们可以通过执行以下命令来运行此任务:

gradle copyDependencies