1.1 何为 Maven

Maven 这个词可以翻译为“知识的积累”,或者翻译成“专家”或“内行”。本书我们介绍Maven 这一跨平台的项目管理工具。作为 Apache 组织中一个颇为成功的开源项目,Maven主要服务于基于 Java 平台的项目构建,依赖管理,项目信息管理。无论是小型的开源类库项目,或者是大型的企业级应用,无论是传统的瀑布式开发,或者是流行的敏捷模式,Maven都十分适用。

1.1.1 何为构建(Build )

不管你有没有意识到,构建是每个程序员每天都在做的工作。早晨跑到公司,吃完手里的早饭吃后,我们从源码库签出最新的源码,然后跑一下单元测试,发现有一些失败的测试,于是找相关的同事一起调试一下,修复了错误代码。接着回到自己的工作上来,编写自己的单元测试及产品代码,我们会感激 IDE 随时报出的编译错误提示,忙到午饭时间,代码编写得差不多了,测试也通过了,心满意足的吃饭休息。下午先昏昏沉沉得开了个例会,完了喝杯咖啡继续工作。刚才会上经理要求看测试报告,OK,找了相关工具集成进 IDE,生成了像模像样的测试覆盖率报告,发了封电子邮件给经理,松了口气。同时看到 QA 那边又发过来了几个 bug,没办法,先本地重现再说,于是熟练的点击 IDE 一些按钮生成了一个 WAR 包,部署到 web 容器下,启动容器。看到熟悉的界面了,遵循 bug 报告,一步步重现了 bug……快下班的时候,bug 修好了,提交代码,通知下 QA,愉快的结束了一天的工作。如果仔细总结下,我们会发现除了编写源代码,每天工作的相当一部分时间花在了编译,运行单元测试,生成文档,打包,部署等等繁琐且不起眼的工作上,这就是构建。我们往往觉察不到这部分时间的流逝,其实,只要稍微花点心思,这一系列的工作完全是可以自动化的。
软件的构建可以像全自动流水线一样,只要一条简单的命令,所有繁琐的步骤都能够自动完成,我们很快就能得到最终的结果。

1.1.2 Maven 是优秀的构建工具

前面介绍了 Maven 的用途之一是服务于构建,它是一个异常强大的构建工具,它帮我们自动化构建过程,从清理,编译,测试,到生成报告,到打包,部署。我们不需要,也不应该一遍又一遍的输入命令,一次又一次的点击鼠标。我们要做的是使用 Maven 配置好项目,然后输入简单的命令如 mvn clean install,Maven 会帮我们处理那些繁琐的任务。Maven 是跨平台的,这意味着无论你是在 windows 上,还是在 Linux 或者 Mac 上,你都可以使用同样的命令,构建同样的项目,得到同样的结果。我们总是不停在寻找方法避免重复,设计的重复,编码的重复,文档的重复,当然还有构建的重复。Maven 最大化的消除了构建的重复,它抽象了构建生命周期,并且为绝大部分的构建任务提供了已实现的插件,我们不再需要定义过程,我们甚至不需要再去实现这些过程中的一些任务。最简单的例子是测试,我们没必要告诉Maven去测试,我们更不需要告诉Maven如何运行测试,我们只需要遵循 Maven 的约定编写好测试用例,当我们运行构建的时候,这些测试便会自动运行。想象一下,Maven 抽象了一个完整的构建生命周期模型,这个模型吸取了大量其它构建脚本,
构建工具的优点,总结了大量项目的实际需求。遵循这个模型,我们可以避免很多前人的错误,我们可以直接使用大量成熟的 Maven 插件来完成我们的任务(很多时候你可能都不知道你在使用 Maven 插件)。此外,如果你真得有非常特殊的需求,你也可以轻松实现自己的插件。
Maven 还有一个优点是它能帮助我们标准化构建过程,在 Maven 之前,可能十个项目有十种构建的方式,现在,只要是使用了 Maven,所有项目的构建命令都是简单一致的,这极大的减免了不必要的学习成本,并能帮助促进项目团队的标准化。综上我们可以看到,Maven 作为一个构建工具,它不仅仅能帮我们自动化构建,它还能够抽象构建过程,提供构建任务实现,它跨平台,并且对外提供了一致的操作接口,这一切足以
使它成为优秀的,流行的构建工具。

1.1.3 Maven 不仅仅是构建工具

很多人仅仅认识到 Java 是一门编程语言,但 Java 不只如此,它还是一个平台,通过 JRuby和 Jython,程序员们可以在 Java 平台上编写和运行 Ruby 和 Python 程序。这里我也想提醒读者,Maven 不仅仅是构建工具,它还是一个依赖管理工具,还是一个项目信息管理工具。它提供了中央仓库,并能帮你自动下载构件。
在这个开源的年代里,几乎任何一个 Java 应用都会借用一大堆第三方的开源类库,这些类库都通过依赖的方式引入到项目中来。随着依赖的增多,版本不一致,版本冲突,依赖臃肿等等问题都会显露出来。手工解决这些问题是一件枯燥乏味,确有不得不面对的事情。Maven提供了一个优秀的解决方案,它通过一个坐标系统准确定位每一个构件(Artifact),也就是用,通过一组坐标,Maven 能够找到任何一个 Java 类库如 jar 文件,Maven 给这个类库世界引入经纬,带来的秩序,于是我们就能借助它帮助我们有序的管理依赖,轻松的解决那些繁杂的依赖问题。
Maven 还能帮助我们管理原本分散在项目各个角落的项目信息,包括项目描述,开发者列表,版本控制系统地址,许可证,缺陷管理系统地址,等等。这些微小的变化看起来很琐碎,并不起眼,但往往在不知不觉中节省了我们大量的寻找信息的时间。这些还只是直接的项目信息,通过 Maven 自动生成的站点和一些已有的插件,我们还能够轻松获得项目文档,测试报告,静态分析报告,源码版本日志报告,等等非常具有价值的项目信息。
Maven 还给全世界的 Java 开发者提供了一个免费的中央仓库,在这里,我们可以找到几乎任何的流行开源类库,通过一些 Maven 的衍生工具如 Nexus,我们还能对其进行快速得搜索。只要定位了坐标,Maven 还能够帮我们自动下载。不再需要一个个的点击开源项目首页,不再需要寻找下载链接,不再需要手工的去下载。

1.2 为什么需要 Maven

Maven 不是 Java 领域唯一的构建管理的解决方案。我们将通过一些简单的例子解释 Maven的必要性,并介绍其它构建解决方案,如 IDE, Make 和 Ant,并将它们与 Maven 作一些比较。

1.2.1 组装 PC 和品牌 PC

我初中时候开始接触计算机,到了高中更是疯狂的想要一台自己的计算机,终于我得到了我的第一台计算机,它是赛扬 733 的。这是一个漫长的过程,我阅读了大量的杂志以了解各类配件的优劣,CPU,内存,主板,显卡,甚至是声卡,我都仔细挑选,后来还跑了很多商家,调货,讨价还价,组装好,接着自己装操作系统,驱动程序……我很享受这个过程,虽然这花费了我大量时间,而且事实证明最后装出来的机器稳定性也不怎样。一年前我需要配一台工作站,这时候我已经没有太多时间去研究电脑配件了,我也变懒了。
我选择了某知名 PC 供应商的在线商店,大概浏览了一下主流的选择,点击选择了我需要的配置,然后下单,付款。接着 PC 供应商会帮我组装电脑,安装操作系统和驱动程序,一周后,物流公司将我的电脑送到了家里,我简单地连接了显示器电源,就能直接使用了。这节省了我大量的时间,而且这台电脑十分稳定,我知道商家在把电脑发送给我之前已经进行了很好的测试。哦,对了,我还能享受两年的售后服务,虽然这是收费的。使用脚本建立高度自定义的构建系统就像买组装 PC,耗时费力,结果也不一定很好。当然,
你可以享受从无到有的乐趣,但恐怕实际项目中没法给你那么多时间。使用 Maven 就像购买品牌 PC,省时省力,并能得到成熟的构建系统,你还能得到来自于 Maven 社区的大量支持。唯一于购买品牌 PC 不同的是,Maven 是开源的,你无须为此付费,如果需要,你还是能去了解 Maven 是如何工作的。而我可不知道那些 PC 巨头的商业秘密。

1.2.2 IDE 不是万能的

当然,我们无法否认优秀的 IDE 帮助我们大大提高了开发效率。当前主流的 IDE 如 Eclipse和 NetBeans 都提供了强大的文本编辑,调式,甚至重构功能。虽然使用简单的文本编辑器加上命令行也能完成几乎任何开发工作,但很少有人愿意那样做。然而 IDE 是有其天生缺陷的:

  • IDE 依赖大量的手工操作,编译,测试,或者代码生成,等等这些工作都是相互独立的,很难一键完成所有工作。而手工劳动往往就意味着低效,意味着容易出错。
  • 很难在项目中统一所有的 IDE 配置,每个人都有自己的喜好。也正是由于这个原因,一个在机器 A 上可以成功运行的任务,到了机器 B 的 IDE 中,可能就会失败。
    我们应该合理利用 IDE,而不是过多的依赖它。对于构建这样的任务,在 IDE 中一次次点击鼠标是愚蠢的行为。Maven 是这方面的专家,而且主流 IDE 如 Eclipse 和 NetBeans 都集成了Maven,这样,我们就可以在 IDE 中方便地运行 Maven 执行构建。

1.2.3 Make

Make 也许是最早的构建工具,它由 Stuart Feldman 于 1977 年在 Bell 实验室创建。他也因此于 2003 年获得了 ACM 国际计算机组织颁发的软件系统奖。目前 Make 有很多衍生实现,包括最流行的 GNU make,以及 BSD Make,还有 Windows 平台的 Microsoft nmake 等等。Make 由一个名为 Makefile 的脚本文件驱动,该文件使用 Make 自己定义的语法格式,其基本组成部分为一系列规则(Rules),而每一条规则的构成部分又包括:目标(Target),依赖(Prerequisite),和命令(Command)。Makefile 基本的结构如下:

TARGET… : PREREQUISITE…
COMMAND
….
…

Make 通过一系列目标和依赖将整个构建过程串联起来,同时利用本地命令完成每个目标的实际行为。Make 的强大之处在于它可以利用一切系统的本地命令,尤其是对于 Unix/Linux系统来说,丰富的功能强大的命令能够帮助 Make 快速高效的完成任务。
但是,Make 将自己和操作系统绑定在一起了,也就是说,使用 Make,你就不能实现(至少很难)跨平台的构建。这对于 Java 来说是非常不友好的。此外,Makefile 的语法也成问题,很多人抱怨他们 Make 构建失败的原因往往是一个难以发现的空格或 Tab 使用错误。

1.2.4 Ant

Ant 不是指蚂蚁(英文中 Ant 是蚂蚁的意思),它意指“另一个整洁的工具(Another Neat Tool)”,它最早用来构建著名的 Tomcat。其作者 James Duncan Davidson 创作该工具的动机就是因为受不了 Makefile 的语法格式。我们可以将 Ant 看成是一个 Java 版本的 Make,也正是使用了Java,Ant 是跨平台的;此外,Ant 使用 XML 定义构建脚本,这相对于 Makefile 来说也更加的友好。
与 Make 类似,Ant 有一个构建脚本 build.xml,如下:

<?xml version="1.0"?>
<project name="Hello" default="compile">
    <target name="compile" description="compile the Java source code to class files">
        <mkdir dir="classes"/>
        <javac srcdir="." destdir="classes"/>
    </target>
    <target name="jar" depends="compile" description="create a Jar file ">
        <jar destfile="hello.jar">
            <fileset dir="classes" includes="**/*.class"/>
            <manifest>
                <attribute name="Main‐Class" value="HelloProgram"/>
            </manifest>
        </jar>
    </target>
</project>

Build.xml 的基本结构也是目标(target),依赖(depends),以及实现目标的任务。比如上面的脚本中,jar 目标用来创建应用程序 jar 文件,该目标依赖于 compile 目标,后者执行的任务是创建一个名为 classes 的文件夹,编译当前目录的 java 文件至 classes 目录。Compile 目标完成后,jar 目标再执行自己的任务。Ant 有大量内置的 java 实现的任务,这保证了其跨平台的特质,同时,Ant 也有特殊的任务 exec 来执行本地命令。
和 Make 一样,Ant 也都是过程式的,开发者显式的指定每一个目标,及完成该目标所需要执行的任务。针对每一个项目,开发者都需要重新编写这一过程,这里其实隐含着很大的重复。而 Maven 是声明式的,项目构建过程以及过程各个阶段所需的工作都由插件实现,并

<?xml version="1.0"?>
<project name="Hello" default="compile">
    <target name="compile" description="compile the Java source code to class files">
        <mkdir dir="classes"/>
        <javac srcdir="." destdir="classes"/>
    </target>
    <target name="jar" depends="compile" description="create a Jar file ">
        <jar destfile="hello.jar">
            <fileset dir="classes" includes="**/*.class"/>
            <manifest>
                <attribute name="Main‐Class" value="HelloProgram"/>
            </manifest>
        </jar>
    </target>
</project>

TARGET… : PREREQUISITE…
COMMAND
且大部分插件都是现成的,开发者只需要声明项目的基本元素,Maven 就执行内置的,完整的构建过程,这在很大程度上消除了重复。Ant 是没有依赖管理的,所以很长一段时间 Ant 用户都不得不手工管理依赖,那是一个令人头疼的问题,好在现在 Ant 的用户可以借助 Ivy 管理依赖。而对于 Maven 用户来说,依赖管
理是理所当然的,Maven 不仅仅内置了依赖管理,更拥有一个可能拥有全世界最多 Java 开源软件包的中央仓库,Maven 用户无须进行任何配置就可以直接享用。

1.2.5 现状,困惑

(注:该小节内容整理自网友 Arthas 最早在 Maven 中文 MSN 群中的讨论,在此致谢)小张是一家小型民营软件公司的程序员,他所在的公司要开发一个新的 Web 项目。大家一致协商确定使用 Spring,iBatis 和 Tapestry。Jar 包哪里找呢?公司里估计没有人能把 Spring,iBatis 和 Tapstry 所使用的 jar 包一个不拉的找出来。大家的做法是,先到 Spring 站点去找个spring‐with‐dependencies,然后去 iBatis 把所有列出来的 jar 也拉下来,同理还有 Tapstry,Apache commons…。项目还没有开发呢,WEB‐INF/lib 下已经有近百个 jar 了,带版本号的,不带版本号的,有用的,没用的,冲突的,怎一个乱字了得!随着项目的开发,小张不时地发现版本错误的问题,也遇到过版本冲突,他只能硬着头皮一个个解决。项目开发到一半,经理发现最终部署的应用体积实在太大了,要求小张去掉一些没用的 jar 包,于是小张只能加班加点的一个个删选……
小张隐隐地觉得这些依赖需要一个框架或者系统来进行管理。小张喜欢学习流行的技术,前几年 Ant 十分流行,于是他也学了,并成为了公司这方面的专家。小张知道,Ant 打包,无非就是创建目录,复制文件,编译源代码,使用一堆任务如 copydir,fileset,classpath,ref,target,最后再 jar,zip,war,然后打包就成功了。项目经理发话了“兄弟们,新项目来了,小张,你来写 Ant 脚本”“是,保证完成任务!”接着,小张继续创建一个新的 XML 文件。target clean; target compile;target jar; … 他没有发现,他写的这么多的 Ant 脚本中,有多少是重复劳动,有多少内容在一个又一个项目中重现。既然都差不多,有些甚至完全相同,为什么每次都要重新编写?终于有一天,小张意识到了问题,并想出来复用 Ant 脚本,于是开会时他说:“以后就都用
我这个规范的 Ant 脚本吧,只要新的项目遵循我定义的目录结构就可以了”经理一听觉得很有道理,“嗯,确实是个进步。”这时新来的研究生发言了:“经理,用 Maven 吧,这个在开源社区很流行,比 Ant 更方便”。
小张一听很惊讶,Maven 真比自己的“规范化 Ant”强大?其实他不知道自己只是在重新发明轮子,Maven 已经有一大把现成的插件,全世界人都在用,你自己都不用写什么东西。为什么没人说:“我自己写的代码最灵活,所以我不用 Spring,我自己实现 IoC,我不用Hibernate,我自己封装 JDBC”?

1.3 Maven 与极限编程

极限编程(XP)是近些年在软件行业红得发紫的敏捷开发方法,它强调拥抱变化。该软件开发方法的创始人 Kent Beck 提出了 XP 所追求的价值,实施原则,以及推荐实践。让我们看一下 Maven 是如何适应 XP 的。
首先看一下 Maven 如何帮助 XP 团队实现一些核心价值:

  • 简单: Maven 暴露了一组一致、简洁的操作接口,能帮助团队成员从原来的高度自定义的,复杂的构建系统中摆脱出来。使用 Maven 现有的成熟,稳定的组件,也能简化构建系统的复杂度。
  • 交流与反馈: 与版本控制系统结合后,所有人都能执行最新的构建并快速得到反馈,此外,自动生成的项目报告也能帮助成员了解项目的状态,促进团队的交流。此外,Maven 更能无缝的支持或者融入到一些主要的 XP 实践中去:
  • 测试驱动开发( TDD ): TDD 强调测试先行,所有产品都应该由测试用例覆盖。而测试是 Maven 生命周期的最重要的组成部分之一,并且 Maven 有现成的成熟的插件支持业界流行的测试框架如 JUnit 和 TestNG。
  • 十分钟构建: 十分钟构建强调我们能够随时快速的从源码构建出最终的产品。这正是Maven 所擅长的,只需要一些配置,之后一条简单的命令就能让 Maven 帮你清理,编译,测试,打包,部署,得到最终的产品。
  • 持续集成( CI ): CI 强调项目以很短的周期(如 15 分钟)集成最新的代码。实际上 CI的前提是源码管理系统和构建系统。目前业界流行的 CI 服务器如 Hudson 和CruiseControl 都能很好的和 Maven 进行集成。这也就是说,使用 Maven 后,持续集成会变得更加方便。
  • 富有信息的工作区: 这条实践强调开发者能够快速方便的了解到项目的最新状态。当然,Maven 并不会帮你把测试覆盖率报告贴到墙上,也不会在你的工作台上放个鸭子告诉你构建失败了。不过使用 Maven 发布的项目报告站点,并配置你需要的项目报告如测试覆盖率报告,这都能帮你发信息推送到开发者眼前。
  • 上述的这些实践并非只在 XP 中适用,事实上,除了其他敏捷开发方法如 SCRUM 之外,几乎其它任何软件开发方法都能借鉴这些实践,这也就是说,Maven 能够很好的支持几乎任何软件开发方法。比如说在传统的瀑布模型开发中,项目需要顺序经历需求开发,分析,设计,编码,测试,及集成发布阶段。从设计和编码阶段,我们就可以使用 Maven 来建立项目的构建系统,在设计阶段,我们也完全可以针对设计开发测试用例,然后再编写代码来满足这些测试用例。而有了自动化构建系统,我们可以节省很多手动的测试时间,此外,尽早的使用构建系统集成团队的代码,对项目也是百利而无一害。最后,Maven 也能帮助我们快速的发布项目。

1.4 被误解的 Maven

C++之父 Bjarne Stroustrup 说说一句话“有两种计算机语言:一些语言天天被人骂,还有一些没人用”。当然这话也不全对,大红大紫的 Ruby 不仅有人用,而且骂得人也少。不过用户最多的 Java 得到的骂声就不绝于耳了。Maven 的用户可不少,它的邮件列表目前在 Apache项目中排在第四位:http://www.nabble.com/Apache‐f90.html。
让我们看看 Maven 受到了哪些质疑,而这里我也对这些质疑逐一解释:
“ Maven 对于 IDE 的支持如 Eclipse 和 IDEA 较差, bug 多且不稳定。” Maven 相对于 JUnit,Ant 来说,显得比较年轻,因此 IDE 集成等衍生产品还不够全面和成熟。但是,我们一定要知道,使用 Maven 最高效的方式永远是命令行,IDE 对于自动化构建有着天生的缺陷。此外,我们也看到 eclipse 的 maven 插件——m2eclipse 是一个比较优秀和成熟的工具,此外,Netbeans 也在积极的为集成 Maven 而努力。
“ Maven 采用了一个糟糕的插件系统来执行构建,新的,破损的插件会让你的构建莫名其妙的失败”
自 Maven 2.0.9 开始,所有核心的插件都被设定了稳定版本,这意味着日常使用 Maven 几乎不会受到不稳定插件的影响。此外,Maven 社区也提倡为任何你使用的插件设定稳定的版本,
如果用户有着好的实践不去采纳,遇到了问题就抱怨,这未免不够公允。“缺乏文档是理解和使用 Maven 的一个主要障碍” 嗯,这是事实。Maven 官方站点的文档十分凌乱,各种插件的文档更是需要费力寻找。
Sonatype 编写的《Maven 权威指南》很好的改善了这一状况。本书编写的目的也是帮助大家理解和使用 Maven。“ Maven 过于复杂,它就是构建系统的 EJB2 ”
不要指望 Maven 十分简单,这几乎是不可能的,Maven 是用来管理项目的,想想看,清理,编译,测试,打包,发布,以及一些自定义的过程。这本身就是件复杂的事情。目前 Java社区还有比 Maven 更强大,更简单的构建工具么?答案是否定的。我们可以尝试去帮助Maven 变得更简单,而绝不是抛弃它,然后自己实现一套更加复杂的构建系统。“ Maven 的仓库十分混乱,当无法从仓库中得到需要的类库时,我需要手工下载复制到本
地仓库中”
Maven 的中央仓库确实不完美,你也许会发现某个 jar 包出现在两个不同的路径下,这不是Maven 的错,这是开源项目本身改变自身的坐标。再者,如果没有中央仓库,你将不得不去开源项目首页寻找下载链接,这不是更费事么?要知道现在有很多的Maven仓库搜索服务。无法从中央仓库找到你需要的类库?由于许可证等因素,这是完全有可能的,这时你需要做的是建立一个组织内部的仓库服务器,你会发现这会给你带来许多意想不到的好处。

1.4 小结

本章只是从概念上简单的介绍了一下 Maven,通过本章,你应该大致了解 Maven 是什么,它有什么用途。这里我还将 Maven 与其它流行的构建工具如 Make 和 Ant 做了一些比较和分析,如果你没用过 Maven,但有 Make 或者 Ant 的使用经验,相信这能帮助你更好的了解和对比所有这些工具。将 Maven 和极限编程结合起来分析是为了让大家从另一个角度来了解 Maven,毕竟软件的开发离不开对于软件过程的理解。本章最后还收集一些用户对 Maven 的误解,并逐条分析解释,希望能够让大家消除误解,积极的接受 Maven。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐