标准的重要性已不用过多强调,想象一下,如果不是所有程序员都基于HTTP协议开发Web应用,互联网会乱成怎样。各个版本的IE、Firefox等浏览器之间的差别已经让很多开发者头痛不已。而Java成功的重要原因之一就是它能屏蔽大部分操作系统的差异,XML流行的原因之一是所有语言都接受它。Maven当然还不能和这些既成功又成熟的技术相比,但Maven的用户都应该清楚,Maven提倡“约定优于配置”(Convention Over Configuration),这是Maven最核心的设计理念之一。
那么为什么要使用约定而不是自己更灵活的配置呢?原因之一是,使用约定可以大量减少配置。先看一个简单的Ant配置文件,见代码清单8-23。
代码清单8-23 构建简单项目使用的Ant配置文件
这段代码做的事情就是清除构建目录、创建目录、编译代码、复制依赖至目标目录,最后打包。这是一个项目构建要完成的最基本的事情,不过为此还是需要写很多的XML配置:源码目录是什么、编译目标目录是什么、分发目录是什么,等等。用户还需要记住各种Ant任务命令,如delete、mkdir、javac和jar。
做同样的事情,Maven需要什么配置呢?Maven只需要一个最简单的POM,见代码清单8-24。
代码清单8-24 构建简单项目使用的Maven配置文件
这段配置简单得令人惊奇,但为了获得这样简洁的配置,用户是需要付出一定的代价的,那就是遵循Maven的约定。Maven会假设用户的项目是这样的:
·源码目录为src/main/java/
·编译输出目录为target/classes/
·打包方式为jar
·包输出目录为target/
遵循约定虽然损失了一定的灵活性,用户不能随意安排目录结构,但是却能减少配置。更重要的是,遵循约定能够帮助用户遵守构建标准。
如果没有约定,10个项目可能使用10种不同的项目目录结构,这意味着交流学习成本的增加,当新成员加入项目的时候,它就不得不花时间去学习这种构建配置。而有了Maven的约定,大家都知道什么目录放什么内容。此外,与Ant的自定义目标名称不同,Maven在命令行暴露的用户接口是统一的,像mvn clean install这样的命令可以用来构建几乎任何的Maven项目。
也许这时候有读者会问,如果我不想遵守约定该怎么办?这时,请首先问自己三遍,你真的需要这么做吗?如果仅仅是因为喜好,就不要耍个性,个性往往意味着牺牲通用性,意味着增加无谓的复杂度。例如,Maven允许你自定义源码目录,如代码清单8-25所示。
代码清单8-25 使用Maven自定义源码目录
该例中源码目录就成了src/java而不是默认的src/main/java。但这往往会造成交流问题,习惯Maven的人会奇怪,源代码去哪里了?当这种自定义大量存在的时候,交流成本就会大大提高。只有在一些特殊的情况下,这种自定义配置的方式才应该被正确使用以解决实际问题。例如你在处理遗留代码,并且没有办法更改原来的目录结构,这个时候就只能让Maven妥协。
本书曾多次提到超级POM,任何一个Maven项目都隐式地继承自该POM,这有点类似于任何一个Java类都隐式地继承于Object类。因此,大量超级POM的配置都会被所有Maven项目继承,这些配置也就成为了Maven所提倡的约定。
对于Maven 3,超级POM在文件$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路径下。对于Maven 2,超级POM在文件$MAVEN_HOME/lib/maven-x.x.x-uber.jar中的org/apache/maven/project/pom-4.0.0.xml目录下。这里的x.x.x表示Maven的具体版本。
超级POM的内容在Maven 2和Maven 3中基本一致,现在分段看一下,见代码清单8-26。
代码清单8-26 超级POM中关于仓库的定义
首先超级POM定义了仓库及插件仓库,两者的地址都为中央仓库http://repo1.maven.org/maven2,并且都关闭了SNAPSHOT的支持。这也就解释了为什么Maven默认就可以按需要从中央仓库下载构件。
再看以下内容,见代码清单8-27。
代码清单8-27 超级POM中关于项目结构的定义
这里依次定义了项目的主输出目录、主代码输出目录、最终构件的名称格式、测试代码输出目录、主源码目录、脚本源码目录、测试源码目录、主资源目录和测试资源目录。这就是Maven项目结构的约定。
紧接着超级POM为核心插件设定版本,见代码清单8-28。
代码清单8-28 超级POM中关于插件版本的定义
由于篇幅原因,这里不完整罗列,读者可自己找到超级POM了解插件的具体版本。Maven设定核心插件的原因是防止由于插件版本的变化而造成构建不稳定。
超级POM的最后是关于项目报告输出目录的配置和一个关于项目发布的profile,这里暂不深入解释。后面会有相关的章节讨论这两项配置。
可以看到,超级POM实际上很简单,但从这个POM我们就能够知晓Maven约定的由来,不仅理解了什么是约定,为什么要遵循约定,还能明白约定是如何实现的。