Java Build工具Ant与Maven之比较

没有一件东西能满足你的全部想法除非你自己创造一个出来。同样对于Build工具来说,也许最好的就是你自己写的。每个项目的Build过程都是独特的,而且通常你的项目要用多种方式构建。对Build工具的作者来说,设想每个构建的需求以迎合它基本是不现实的。工具最好就是提供一个灵活的库和可以重用的任务来被我们按需调用,但是这样也远远不够,成品的工具从来不会100%满足你的需求。你会浪费大量的时间让那些任务能按你的想法去工作,但是最后可能不得不自己写一个插件了事。写自己的定制化构建工具是快且容易的,比你担忧的维护量要少很多。构建就是要适合你的项目,而不是别的什么东西,这有什么可担心的。

 

如果你不想自己写构建工具,你可以用Rake。Rake是目前最好的基于Ruby语言编写的Java项目构建工具。Rake提供了一堆标准的方法来完成通常的构建任务,任何其他的任务都可以通过Ruby快速实现。Rake是用一个真正的程序语言(Ruby)来写构建脚本,这是它优于其它工具的地方。当然,Rake还有许多其它的优点,但都没有这个重要。

因此,你应该自己写构建工具,如果不想自己写,那就用Rake吧。如果你不想转向Rake,你也应该劝说他们转向正确的工具。假如技术上的决定被其它东西(办公室政?治)左右,假如不允许你这样做,那就离开这个项目,没什么好说的。

如果你没有勇气退出,那么使用Ant吧。对Java项目来说,Ant是第二好的构建工具。虽然不如Rake,Ant仍然可以说是最牛?B的构建工具。Ant成熟且稳定,快速且工具集丰富。Ant能让你把复杂的项目构建过程通过合身的脚本表达出来,虽然不全都是那么简单。

总而言之,写自己的构建工具,不行就转向Rake,不让转就力争或者辞职到其它让你用Rake的地方。如果全都不行,就用Ant吧,直到哪天有个公司使用Rake就跳过去。

就这些,这就是我能建议你们的唯一选择。因为,你们绝对不要、永远不要,在任何情况下试图使用Maven。

Maven的构建本身就是一个无穷无尽的绝望的怪圈,它一点一点地把你拖进万劫不复的Maven地狱。起初你就用了10分钟就搭好了Maven环境,你因此手舞足蹈。可是随着项目的进展,你的配置要增加,原来的pom.xml肯定是不够用了。为了让项目按你想要的方向上发展,你要一点点地增加配置。用不了多久,你就会第一次鬼打墙般地遇到问题。(用“鬼打墙”这个词,我的意思是“突然搞得人很头疼”。之所以说“第一次”,因为将来你还会不断地鬼打墙)。终于有一天,你会为了解决一个燃眉之急而临时凑合地改了改这个已经乱的不成样子的pom.xml。你可能还对Maven满意了好一阵子...直到又有一个问题露出了恶心的小头(这让我想起女孩青春期挤脸上粉刺的情景。当她挤掉脸上出现的第一个粉刺豆豆,她会好有成就感。可是当豆豆越来越多,挤也挤不干净的时候,她就不再对少女时期洁白无瑕的脸庞抱有幻想了——译者注)。这让我联想到希腊神话里的悲剧,你就是被打入地狱的灵魂,带着永世的诅咒——你的基于Maven的构建过程。

严肃地说,Maven就是坏主意的糟透了的实现。我相信总是有人认为Maven即使不性感,起码也够清晰智能,可是,现实上Maven的实现毫无这些迹像。实际上,Maven是彻头彻尾地糟透了,已经成为了一个著名的反例——告诉人们如何不要去构建软件。当构建正在以Maven相反的方式工作,你知道这是多么的可怕。

例如从Maven's Surefire 插件输出的测试结果。(Surefire插件用来在Maven构建生命周期的测试阶段执行一个应用的单元测试。它会产生两种不同形式的测试结果报告:纯文本;.xml文件。默认情况下,这些文件生成在工程${pom文件所在的目录}/target/surefire-reports目录下。——译者注)。只要测试通过一切万事大吉,但是如果出了错误,Surefire的测试报告就是一个调试的梦魇。只有失败的测试类的名称被记录下来。你必须手动地查看target/surefire-reports/目录下的日志文件,这些日志文件是按每个测试类组织的。如果多个测试类都失败了,你就必须分别地检查多个日志文件。看上去是个小事情,累积起来会让人一肚子怨气,从而生产效率下降。

Maven主张的约定优于配置,其实是个谎言。Maven支持的唯一约定是:编译,运行单元测试,生成jar包。做其它任何事情就开始要配置了:生成war包?要配置;从命令行运行程序?要配置;运行验收测试(把所有系统理论上目前支持的场景都列出来,然后按功能分类测试,如果测试成功,就在此场景中标明“成功”,否则,就标明“失败”。——译者注)或功能测试或性能测试,还是要配置它,但是涉及到不能运行你的单元测试,或者在常规的单元测试阶段不能运行。想要生成项目的代码覆盖率度量?你还要配置,但是你的测试要运行2次(或者可以只运行一次,只是不能在常规的单元测试阶段);尽管是做全方位的测试,它还是经常会报告了0%的代码覆盖率。

谈到配置,Maven的配置语法是Sendmail(在Unix系统的用户中,Sendmail是应用最广的电子邮件服务器。Sendmail作为一种免费的邮件服务器软件,已被广泛的应用于各种服务器中,它在稳定性、可移植性、健壮性等方面很出色。)以来最让人反胃的了——交替正常格式的XML。因此,Maven配置累赘,拗口,难写。在Ruby、Rake的XML或者Ant中1、2行就可以搞定的事情,在pom.xml里要6、7、8行才可以做到(还要假设Maven能做到才行)。

Maven的配置一点也不连贯。一会引用classpath的相对路径,一会用相对于磁盘的绝对路径,一会又用运行于JVM之上的Maven的系统属性。有些绝对路径能被移植是因为Maven知道怎么自动纠正错误,有些纠正不了。有时Maven智能地知道如果递归地构建项目,有时经常又不行。

而且,有些东西在pom中根本无法配置。比如:Maven仓库、服务器、授权凭据等是在settings.xml配置的。既然pom.xml要提交到项目的版本控制仓库,那么Maven把用户密码置于pom.xml文件之外是言之有据的。可是Maven的解决方法太恐怖了:所有的这类配置被放进了一个叫做settings.xml的文件里,而这个文件不在任何的项目目录里。你不能直接地在桌面电脑和笔记本、其它开发者或者你的项目构建服务器之间共享任何这类配置。但是,它却自动地在你工作的每个单独的Maven项目间共享,潜在地在同一台计算机上的不同用户之间的每个Maven项目间共享。当一个新的开发者加入到项目中,就必须手动合并必要的配置信息到原有的settings.xml中。当一个新的代理被加到构建服务器里,还是要手动合并必要的配置信息到原有的settings.xml中。同上,当你迁移到新的机器。任何这些配置需要更新,必须手动地在每个单独的机器上更新。这也是一个在Maven到来之前就已经解决的问题:属性文件。项目组把通用的属性文件提交到版本控制,然后每个开发者在本地的属性文件中覆盖这些信息,本地的属性文件中不提交到版本控制。

所有这些东西在Maven里——那些约定,配置,过程——被以所谓的Maven方式管理。不幸的是,所谓的Maven方式没有文档说明。为了抓住那一定点儿稍纵即逝的信息,往往要在Maven文档里拉网式地搜索,Google,或者买Maven开发者们写的书。(有点类似众里寻她千百度,蓦然回首,不知春归处——译者注)。还有的另外办法来找到所谓的Maven方式就是鬼打墙了。(走着走着被自己要找的东西绊倒了。作者的意思就是这——所谓的Maven方式——就像看不见的边界,试图找到它的办法可能就是不经意地撞到它——译者注)。Maven的构建不是为了灵活,不支持每种可能的构建过程。Maven是为了Apache项目而构建的,假定了每个项目的构建过程都像Apache项目自身一样。这对自愿奉献自己时间的开源库的开发者来说是个好消息,对于这些人,“发布”意味着“上传一个新的zip文件到你的站点为了便于其他人手动发现,下载然后添加到他们自己的项目中。” 其实这是溜须拍马的做法。Rake和Ant可以适应每种构建过程,Maven不能;Maven也许,实际上很可能,它恰恰不能支持你想要的那种构建软件的方式。

还有就是Maven的依赖管理是彻头彻尾地不可挽回的破坏。事实上,我收回我的话;Maven的下载ibiblio(Maven会从ibiblio.org中的公用仓库中同步构件,这个公用仓库下载缓慢、 不稳定,并且不包含一些构件的最新版本,而且不能上传团队私有的构件。——译者注)到用户的home目录,然后一古脑地把所有东西像垃圾一样倒在classpath下。这种傻到让人难以置信的依赖管理策略是极其错误的,从来不应与“依赖管理”混淆。我近来工作的一个Maven工程,它产生出一个51MB的.war文件;当我把它转到Ant下手动配置依赖管理,这个.war文件减小到了17MB。靠,51-17=34=17x2,居然原来三分之二的东西都是Maven给我们制造的无用的废物。

不相干的依赖包不仅仅吃掉硬盘空间,而且会吃掉宝贵的内存。Maven完全就是多吃多占内存的大蠢猪。相对简单的项目,只带有一个父pom和一些子模块,就要求一大堆的JVM内存——通过改变JAVA_OPTS配置来得到,而这些精巧的JAVA_OPTS配置通常你仅仅在产品服务器上才用到。如果整合Maven构建工具到你的IDE,事情可能更糟。它把JVM的最大堆尺寸设成好几百兆,把最大的PermGen尺寸设成几百兆,并启用permgen sweeping,这样类自己会被垃圾回收。(PermGen——Permanent Generation space,是指内存的永久保存区域。这一部分用于存放Class和Meta的信息,Class在被Load的时候被放入PermGen区域,它和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen进行清理,所以如果你的APP会Load很多Class的话,就可能出现PermGen错误。——译者注)。所有这一切仅仅是构建你的项目,或者在IDE里与Maven一起工作。

一个可笑的故事:在上面那个项目里,我曾经经历了一个10分钟的“mvn clean”构建命令,因为Maven愚蠢地想在“rm -rf ./target/”之前做点事情 (参考类似的例子: http://gist.github.com/267553)。实际这个故事一点不可笑;相信我,你不想要一个工具——它在清空你的构建输出目录之前自动下载所有未解析的依赖包。你不想要一个工具——它自动下载所有未解析的依赖包。隔一段时间就下载所有未解析的依赖包让你的构建过程变得不确定。好,我是不确定主义者:这在学校里好玩,不是在工作中。

所有那些不必要的,不想要的网络来回占有了大量时间。每一次构建你都要为Maven的破坏性的包依赖管理付出性能的代价。10分钟的clean构建是恐怖的,但是每次构建都多几分钟就更糟了。我估算的每次构建的平均额外消耗时间大约是一分钟,这是基于一个事实:我把Maven项目迁移到Ant上,平均构建时间缩短了2.5分钟到1.5分钟;类似地把Ant项目迁移到Maven上,平均构建时间增长了2~3分钟。

你无法控制,也看不清哪些包被哪些包指定。构建可能中断因为不同的Maven版本在不同的时间会下载不同的包。你的本地构建有一天也会中断,当你的依赖包的依赖包偶然地发布了新的,不向后兼容的改变而忘记了敲上版本号;那些失败是无辜的。更容易发生的情况是你的项目A依赖了某个特定版本的其它项目B,而这个项目B反过来依赖某个最后版本的另外的项目C,这时你还蒙在鼓里,即使下游包提供者敲上了正确的版本号。每次发布依赖包的依赖包都可能造成新的错误,从而会浪费你几个小时去追踪为何构建失败。

但是Maven比这甚至还要糟的是:Maven不仅仅解决你的项目的包依赖,它还自动解决自己带的插件的包依赖。于是现在你不仅仅要担心不同的Maven实例偶然会下载不兼容的东西(或者同一个Maven在不同的时间下载不同的东西),你还要担心你的构建工具在不同计算机的不同时间上的行为不一致。

Maven的残缺不堪的包依赖管理同时带来了一个安全的隐患,既然Maven目前不可能确定包最初来自哪里,被篡改与否。当上传包到代码仓库中,包会被自动数字签名,当Maven下载包时会自动验证签名,可是Maven无疑地信任代码仓库中的签名。当前Maven的包安全是由Maven开发者控制的,他们有对ibiblio权威信息库写的权限。但是无法知道你下载的包的所有依赖包的代码仓库是不是中毒了,没有办法知道你的本地仓库缓存是不是中毒了,也无法知道你本地仓库缓存中里的包来自哪里和谁上传了它们。

综上问题并不是粗心的程序员造成的,也无法通过仓库管理员锁定每个Maven需要的包来解决。如果Maven假设人从来不犯错误,那它就是一个破烂货(Maven is broken and wrong)如果Maven要使用者明确指定每个依赖的版本,每个依赖的依赖,来减少下载不兼容的包的可能性,那它就是一个破烂货。如果Maven非得通过一个第三方工具来阻止它连接到网络并自动下载那些乱七八糟的狗?屎,那它就是一个破烂货。如果Maven无视每次构建都要连接到网络,为任何更新都检查每个依赖项,然后自动下载它们,从而让构建过程变得缓慢不堪,那它就是一个破烂货。如果Maven在我的办公室和家里的笔记本电脑上表现的不一致,那它就是一个破烂货。如果Maven连删除一个目录都需要互联网,那它就是一个破烂货。Maven就是一个破烂货。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值