in Java

也说Maven

最近换了个离家近的工作,原以为会多出一些时间来学习,可偏偏还挺忙,导致又是很长时间没有更新Blog,习惯性地惭愧:(

新工作的内容涉及到跨平台编程,主要涉及到的语言是C#和Java。老话说得好:隔行如隔山。在我从事的行业,跨平台就意味着巨大的差异,这不,我这样的资深微软系码工,一用Eclipse打开业务项目,一只拦路虎便扑面而来:编译器找不到依赖的包,无法编译——因为项目使用了Maven,一个我不熟悉的管理工具。

工欲善其事,必先利其器。花了大半月时间,在 Maven: The Definitive Guide 的帮助下,算是对这个工具有了一些粗浅的认识,迫不及待的写下来,也算是对认知的梳理吧:

Maven: The Definitive Guide

Maven基于这样一个理念进行项目的管理:

任何项目,通过几个标识符的交叉坐标,能被唯一的标识。在此基础之上,按软件项目的特性划分阶段(Phases),在每个阶段,Maven 调用相应插件(Plugins) 对每个阶段的目标 (Goals)提供配置管理和实作调用。

有些抽象不是,回到根本的问题上来,我们为什么要用它呢?它解决了什么问题?

之前通过一些拐弯抹角的信息收集,我认为它一个用来管理包依赖关系的工具,而这也确实是它的主要用途之一:在大型项目中,动辄引用数十个源码项目或包是司空见惯的现象,手工添加引用是不胜其烦 (类似于Visual studio 中的 Add references),出错的机率也大。Maven 在这方面走得要远一些,基于前述的 交叉坐标 系统,将项目的依赖引用关系得以文本化,使用者只需提供 项目对象模型 (POM,Maven认可的描述性配置文件),Maven便能知道怎么处理项目间的关系,甚至还能管理依赖的依赖,传递引用等等。

除了依赖关系管理,它还能做构建工作,单元测试,打包,部署,运行调试用的Web服务器,生成报表—— 其实它并不自己来做这些事,而是调用面向不同任务的插件。

所以,Maven项目官网是这么说的:“Apache Maven is a software project management and comprehension tool”。

面对如此抽象而宏大的自述,总是会头疼,要知道我是来学习工具的,你却告诉我你是干项目管理的,WTF!

所以学至半程,我决定抛开一些用不到的花边,回过头进行简化归纳:

  1. Maven在大部分时候,是一个依赖管理和包构建工具;
  2. Maven使用 GroupId/ ArtifactId/ Version [/ Packaging…] 等元信息来唯一的标识源码项目和包,这些信息交叉形成的标识被称之为坐标,其中GroupId代表项目的组织,ArtifactId代表项目名称,Version代表项目的版本号。一般而言,GroupId: ArtifactId: Version 这三个元信息交叉就能唯一标识一个项目的特定版本;
  3. Maven调用插件来执行任务时,需要指定插件和目标 (命令形如 Plugin:Goal,比如你要打包一个项目成Jar包,对应的插件叫Jar,目标也叫Jar,命令就是 mvn jar:jar——也可以在配置中指定package阶段使用jar插件,然后调mvn package,它就知道该打Jar包了);不同的插件和目标,实际上是对应到项目的不同生命周期(资源处理、编译、打包、测试、部署……);
  4. Maven会在本地建立一个仓库,来集中管理本地项目引用的包和项目,这个仓库通常位于当前用户目录的.m2/repository下。比如你在项目中引用了 apache 的 velocity 模板渲染引擎,在使用 Maven进行构建时,由于velocity是一个远程的包,Maven会先检查本地仓库是否有velocity 包的描述是否存在,如果不存在,则会进行下载 (这是一个一次性的动作,可能会耗费一些时间,Maven 会继续追踪 velocity 本身的引用依赖,将这个依赖链上的所有包都下载到仓库)
  5. Maven基于 交叉坐标的设计,也会反映到仓库和项目层级上:如仓库,对于一个 velocity  1.7 的包,在目录结构上,它呈现为: ~/.m2/repository/org/apache/velocity/velocity/1.7  (由路径可知,项目的GroupId是org.apache.velocity, ArtifactId为velocity,Version号为1.7),虽繁锁却清晰;如项目结构,一个Maven化过的项目,在文件目录中的每一个层级,必定会有一个 pom.xml 文件,而对于多模块的项目,各自通过Parent(子项目)和 Modules(父项目)来声明关联,而GroupId 和 ArtifactId 可视情况沿用父项目的声明;
  6. Maven项目间的配置传递特性,可用于归纳共用的配置:但凡超过一处的依赖(Dependencies)或者插件(Plugins)引用,或者构造(Buid)设置,都应该将其放至项目共同的父级项目配置中,以减少冗余并降低不可预见的错配,没错,就是DRY原则;

一通梳理过后,我发现自己知道的要比想像中多一些,但知识点凌乱且欠组织,需要进一步的实践来提炼。接下来要深挖的,是Maven的诸多插件和默认值 (像地雷一样分布的 Conventions, but over Configuration),在一个陌生的配置海洋中,找到属于我的救生圈。

对于Java 工具链及其身后庞大的企业级编程阵营,程序员个人是渺小的:这里没有脚本编程的随性和函数式的工巧,也不像微软系自成一体可以随时甩历史包袱,而是——在开源社区最佳实践的引导和大企业联盟沉重历史包袱的双重驱赶下,程序员既要做到思想上的敏捷,又要面临历史遗留问题的考验——Project Object Model好不好?当然好,语义清晰目标明确,那它有什么不好?一如其他配置海洋一般的重。但你站在它的视角想一想又能理解,它认为自己不是Ant,是项目管理工具,它之所以出现取代并成为Ant的超集,就是历史遗留下来的教训所致,Java这个号称企业级的航母需要一个企业级的全周期管理工具,来最小化因程序员的个人发挥所致的项目损失……

说这些无非是因为我头一次遇到一个工具庞大到脑疼。槽也吐了,磕完Maven我就能正儿八经的写点Java项目了,谢天谢地!

打赏作者
您的支持将激励我继续创作!

您的支持将鼓励我们继续创作!

[微信] 扫描二维码打赏

[支付宝] 扫描二维码打赏

Write a Comment

Comment