有人能告诉我 Ant 和 Maven 之间的区别吗?我也从未使用过。我知道它们用于自动化 Java 项目的构建,但我不知道从哪里开始。
9 回答
在Maven: The Definitive Guide中,我在简介中写了关于 Maven 和 Ant 之间的差异的部分标题是“Ant 和 Maven 之间的差异”。这是一个答案,它结合了该介绍中的信息和一些附加说明。
一个简单的比较
我向您展示这个只是为了说明,在最基本的层面上,Maven 具有内置约定。这是一个简单的 Ant 构建文件:
<project name="my-project" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src/main/java"/>
<property name="build" location="target/classes"/>
<property name="dist" location="target"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source " >
<!-- Compile the java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile"
description="generate the distribution" >
<!-- Create the distribution directory -->
<mkdir dir="${dist}/lib"/>
<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file
-->
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
</target>
<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
在这个简单的 Ant 示例中,您可以看到您必须如何准确地告诉 Ant 要做什么。有一个编译目标,其中包括将 src/main/java 目录中的源代码编译到 target/classes 目录的 javac 任务。您必须准确地告诉 Ant 您的源代码在哪里,您希望生成的字节码存储在哪里,以及如何将这一切打包到一个 JAR 文件中。尽管最近的一些发展有助于减少 Ant 的过程性,但开发人员使用 Ant 的经验是编写用 XML 编写的过程性语言。
将前面的 Ant 示例与 Maven 示例进行对比。在 Maven 中,要从一些 Java 源创建 JAR 文件,您需要做的就是创建一个简单的 pom.xml,将源代码放在 ${basedir}/src/main/java 中,然后从命令行运行 mvn install . 实现相同结果的示例 Maven pom.xml。
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
</project>
这就是你在 pom.xml 中所需要的。从命令行运行 mvn install 将处理资源、编译源代码、执行单元测试、创建 JAR 并将 JAR 安装在本地存储库中以便在其他项目中重用。无需修改,您可以运行 mvn site 然后在 target/site 中找到一个 index.html 文件,其中包含指向 JavaDoc 的链接和一些关于您的源代码的报告。
诚然,这是最简单的示例项目。仅包含源代码并生成 JAR 的项目。遵循 Maven 约定且不需要任何依赖项或自定义的项目。如果我们想开始自定义行为,我们的 pom.xml 将会变大,在最大的项目中,您可以看到非常复杂的 Maven POM 集合,其中包含大量插件自定义和依赖声明。但是,即使您的项目的 POM 文件变得更加庞大,它们所保存的信息类型也与使用 Ant 的类似大小项目的构建文件完全不同。Maven POM 包含声明:“这是一个 JAR 项目”和“源代码在 src/main/java 中”。Ant 构建文件包含明确的指令:“这是项目”、“src/main/java
”、“javac
针对这个目录运行”、“将结果放入target/classses
”、“从 .... 创建一个 JAR”等。Ant 必须明确说明该过程,而 Maven 有一些“内置”的东西只知道源代码在哪里以及应该如何处理。
高级比较
这个例子中 Ant 和 Maven 的区别?蚂蚁...
- 没有像通用项目目录结构这样的正式约定,您必须准确地告诉 Ant 在哪里可以找到源代码以及在哪里放置输出。随着时间的推移,非正式的约定已经出现,但它们还没有被编入产品中。
- 是程序性的,你必须准确地告诉 Ant 做什么以及什么时候做。你必须告诉它编译,然后复制,然后压缩。
- 没有生命周期,您必须定义目标和目标依赖项。您必须手动将一系列任务附加到每个目标。
马文在哪里...
- 有约定,它已经知道你的源代码在哪里,因为你遵循了约定。它将字节码放在目标/类中,并在目标中生成一个 JAR 文件。
- 是声明性的。您所要做的就是创建一个 pom.xml 文件并将您的源代码放在默认目录中。Maven 负责其余的工作。
- 有一个生命周期,您在执行
mvn install
. 该命令告诉 Maven 执行一系列序列步骤,直到它到达生命周期。作为整个生命周期旅程的副作用,Maven 执行了许多默认插件目标,这些目标执行诸如编译和创建 JAR 之类的事情。
常春藤呢?
是的,所以像史蒂夫拉夫兰这样的人会读到这个比较并吹犯规。他将讨论答案如何完全忽略了称为 Ivy 的东西,以及 Ant 可以在最新版本的 Ant 中重用构建逻辑的事实。这是真实的。如果您有一群聪明的人使用 Ant + antlibs + Ivy,那么您最终会得到一个设计良好且有效的构建。尽管如此,我非常确信 Maven 是有意义的,但我很乐意将 Ant + Ivy 与一个拥有非常敏锐的构建工程师的项目团队一起使用。话虽如此,我确实认为您最终会错过许多有价值的插件,例如 Jetty 插件,并且您最终会做一大堆您不需要随着时间的推移做的工作。
比 Maven vs. Ant 更重要
- 是您使用存储库管理器来跟踪软件工件。我建议下载 Nexus。您可以使用 Nexus 代理远程存储库,并为您的团队提供一个地方来部署内部工件。
- 您对软件组件进行了适当的模块化。一个大的整体组件很少随着时间的推移而扩展。随着项目的发展,您将需要模块和子模块的概念。Maven 非常适合这种方法。
- 您为构建采用了一些约定。即使您使用 Ant,您也应该努力采用与其他项目一致的某种形式的约定。当一个项目使用 Maven 时,这意味着任何熟悉 Maven 的人都可以选择构建并开始运行它,而不必为了弄清楚如何编译该东西而摆弄配置。
Maven 是一个框架,Ant 是一个工具箱
Maven 是一辆预制的公路车,而 Ant 是一套汽车零件。使用 Ant,您必须制造自己的汽车,但至少如果您需要进行任何越野驾驶,您可以制造合适类型的汽车。
换句话说,Maven 是一个框架,而 Ant 是一个工具箱。如果您对在框架范围内工作感到满意,那么 Maven 就可以了。对我来说,问题是我一直碰到框架的界限,它不会让我出去。
XML 详细程度
tobrien 是一个非常了解 Maven 的人,我认为他对这两种产品进行了非常好的、诚实的比较。他将一个简单的 Maven pom.xml 与一个简单的 Ant 构建文件进行了比较,并提到了 Maven 项目如何变得更加复杂。我认为值得看一下您在简单的实际项目中更有可能看到的几个文件的比较。以下文件代表多模块构建中的单个模块。
一、Maven文件:
<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/maven-4_0_0.xsd">
<parent>
<groupId>com.mycompany</groupId>
<artifactId>app-parent</artifactId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>persist</artifactId>
<name>Persistence Layer</name>
<dependencies>
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>common</artifactId>
<scope>compile</scope>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>domain</artifactId>
<scope>provided</scope>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>${hibernate.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>${commons-dbcp.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc</artifactId>
<version>${oracle-jdbc.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>${easymock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
和等效的 Ant 文件:
<project name="persist" >
<import file="../build/common-build.xml" />
<path id="compile.classpath.main">
<pathelement location="${common.jar}" />
<pathelement location="${domain.jar}" />
<pathelement location="${hibernate.jar}" />
<pathelement location="${commons-lang.jar}" />
<pathelement location="${spring.jar}" />
</path>
<path id="compile.classpath.test">
<pathelement location="${classes.dir.main}" />
<pathelement location="${testng.jar}" />
<pathelement location="${dbunit.jar}" />
<pathelement location="${easymock.jar}" />
<pathelement location="${commons-dbcp.jar}" />
<pathelement location="${oracle-jdbc.jar}" />
<path refid="compile.classpath.main" />
</path>
<path id="runtime.classpath.test">
<pathelement location="${classes.dir.test}" />
<path refid="compile.classpath.test" />
</path>
</project>
tobrien 使用他的示例来说明 Maven 具有内置约定,但这并不一定意味着您最终会编写更少的 XML。我发现事实恰恰相反。pom.xml 比 build.xml 长 3 倍,并且没有偏离约定。事实上,我的 Maven 示例没有显示配置插件所需的额外 54 行。该 pom.xml 用于一个简单的项目。当您开始添加额外的需求时,XML 确实开始显着增长,这对于许多项目来说并不少见。
但是你必须告诉 Ant 该怎么做
当然,我上面的 Ant 示例并不完整。我们仍然需要定义用于清理、编译、测试等的目标。这些目标是在一个通用构建文件中定义的,该文件由多模块项目中的所有模块导入。这让我明白了所有这些东西必须在 Ant 中显式编写,而在 Maven 中是声明性的。
的确,如果我不必显式编写这些 Ant 目标,它会节省我的时间。但是多少时间?我现在使用的通用构建文件是我 5 年前编写的,从那时起只做了轻微的改进。在我对 Maven 进行了 2 年的实验之后,我将旧的 Ant 构建文件从壁橱里拿出来,掸掉了灰尘,然后重新开始工作。对我来说,在 5 年的时间里,必须明确告诉 Ant 做什么的成本加起来不到一周。
复杂
我想提到的下一个主要区别是复杂性及其对现实世界的影响。构建 Maven 的目的是减少负责创建和管理构建过程的开发人员的工作量。为了做到这一点,它必须很复杂。不幸的是,这种复杂性往往会否定他们的预期目标。
与 Ant 相比,Maven 项目的构建人员会花费更多时间:
- 阅读文档:关于 Maven 的文档要多得多,因为你需要学习的东西还有很多。
- 教育团队成员:他们发现向知道的人提问比自己寻找答案更容易。
- 构建故障排除:Maven 不如 Ant 可靠,尤其是非核心插件。此外,Maven 构建是不可重复的。如果您依赖于插件的 SNAPSHOT 版本,这很可能,您的构建可能会在您没有更改任何内容的情况下中断。
- 编写 Maven 插件:插件通常在编写时考虑到特定的任务,例如创建一个 webstart 包,这使得将它们重用于其他任务或将它们组合以实现目标变得更加困难。因此,您可能必须编写自己的一个来解决现有插件集中的空白。
相比之下:
- Ant 文档简洁、全面且集中在一处。
- 蚂蚁很简单。尝试学习 Ant 的新开发人员只需要了解一些简单的概念(目标、任务、依赖项、属性),就能够弄清楚他们需要了解的其余内容。
- 蚂蚁是可靠的。在过去的几年里,Ant 的发布并不多,因为它已经可以工作了。
- Ant 构建是可重复的,因为它们通常是在没有任何外部依赖项的情况下创建的,例如在线存储库、实验性第三方插件等。
- 蚂蚁是全面的。因为它是一个工具箱,所以您可以组合这些工具来执行几乎任何您想要的任务。如果您需要编写自己的自定义任务,这很简单。
熟悉度
另一个区别是熟悉程度。新开发人员总是需要时间来跟上进度。熟悉现有产品在这方面有所帮助,Maven 支持者正确地声称这是 Maven 的好处。当然,Ant 的灵活性意味着您可以创建任何您喜欢的约定。所以我使用的约定是将我的源文件放在一个目录名 src/main/java 中。我编译的类进入一个名为 target/classes 的目录。听起来很熟悉是不是。
我喜欢 Maven 使用的目录结构。我认为这是有道理的。还有他们的构建生命周期。所以我在我的 Ant 构建中使用相同的约定。不仅因为它有意义,而且因为任何以前使用过 Maven 的人都会熟悉它。
仅列出更多差异:
- Ant 没有正式的约定。您必须准确地告诉 Ant 在哪里可以找到源代码、在哪里放置输出等等。
- 蚂蚁是程序性的。你必须准确地告诉 Ant 该怎么做;告诉它编译,复制,然后压缩等。
- Ant 没有生命周期。
- Maven 使用约定。只要您遵循这些约定,它就会自动知道您的源代码在哪里。你不需要告诉 Maven 它在哪里。
- Maven 是声明性的;您所要做的就是创建一个 pom.xml 文件并将您的源代码放在默认目录中。Maven 将负责其余的工作。
- Maven 有一个生命周期。您只需调用mvn install并执行一系列序列步骤。
- Maven 具有关于常见项目任务的智能。要运行测试,只要文件位于默认位置,只需执行mvn test即可。在 Ant 中,您首先需要 JUnit JAR 文件,然后创建一个包含 JUnit JAR 的类路径,然后告诉 Ant 它应该在哪里查找测试源代码,编写一个编译测试源的目标,然后最后执行单元测试与 JUnit。
更新:
这来自Maven:权威指南。对不起,我完全忘了引用它。
Maven还是蚂蚁?是一个与此问题非常相似的问题,它应该可以帮助您回答您的问题。
什么是马文?在官方网站上。
编辑:对于一个新的/未开发的项目,我建议使用 Maven:“约定优于配置”将为您节省大量编写和设置构建和部署脚本的时间。当您使用 ant 时,构建脚本的长度和复杂性往往会随着时间的推移而增长。对于现有项目,很难将它们的配置/布局硬塞到 Maven 系统中。
Maven 既可以作为依赖管理工具——它可以用来从中央存储库或你设置的存储库中检索 jars——也可以作为声明性构建工具。“声明式”构建工具与更传统的 ant 或 make 等构建工具之间的区别在于您配置需要完成的工作,而不是如何完成。例如,您可以在 maven 脚本中说项目应该打包为 WAR 文件,而 maven 知道如何处理它。
Maven 依赖于项目目录布局的约定以实现其“声明性”。例如,它对放置主代码的位置、放置 web.xml 的位置、单元测试的位置等有一个约定,但也可以在需要时更改它们。
您还应该记住,在 maven 中有一个用于运行 ant 命令的插件:
http://maven.apache.org/plugins/maven-ant-plugin/
此外,maven 的原型使项目的启动速度非常快。例如,有一个 Wicket 原型,它提供了一个 maven 命令,您可以运行该命令来获得一个完整的、可立即运行的 hello world 类型的项目。
我可以带一个从未见过 Ant 的人——它build.xml
的 s 写得相当好——他们可以理解发生了什么。我可以让同一个人向他们展示一个 Maven POM,他们不会知道发生了什么。
在一个庞大的工程组织中,人们写到 Ant 文件变得庞大且难以管理。我已经编写了这些类型并清理了 Ant 脚本。它真的是提前了解您需要做的事情,并设计一组可以在 3 年以上的时间内响应变化和扩展的模板。
除非你有一个简单的项目,否则学习 Maven 约定和 Maven 完成工作的方式是相当多的工作。
归根结底,您不能将使用 Ant 或 Maven 的项目启动视为一个因素:这实际上是总拥有成本。组织需要在几年内维护和扩展其构建系统是必须考虑的主要因素之一。
构建系统最重要的方面是依赖管理和表达构建配方的灵活性。如果做得好,它必须有点直观。
我会说这取决于您项目的大小......就个人而言,我会将 Maven 用于需要直接编译、打包和部署的简单项目。一旦您需要做一些更复杂的事情(许多依赖项,创建映射文件......),我会切换到 Ant......
Maven 还包含一个大型的常用开源项目存储库。在构建期间,Maven 可以为您下载这些依赖项(以及您的依赖项依赖项:)),以使构建项目的这一部分更易于管理。