简介
工作之后,用的最多的就是maven,可是也没深入的去研究maven的内部实现原理,今天看了《Maven实战2》
同类产品比较:
创世之初,世上只有Make一种构建工具,后来,其发展为GNU Make。但是,由于需求的不断涌现,码农的世界里逐渐演化出了千奇百怪的构建工具。
当前,JVM生态圈由三大构建工具所统治:
- Apache Ant带着Ivy
- Maven
- Gradel
Ant的主要优点在于对构建过程的控制上。
Gradle的成就可以概括为:约定好,灵活性也高。
Maven的主要优点是生命周期。只要项目基于一定之规,它的整个生命周期都能够轻松搞定,代价是牺牲了灵活性。
maven坐标和依赖
坐标详解
groupId :定义当前Maven项目隶属的实际项目。首先,Maven项目和实际项目不一定是一对一的关系。比如SpringFrameWork这一实际项目,其对应的Maven项目会有很多,如spring-core,spring-context等。这是由于Maven中模块的概念,因此,一个实际项目往往会被划分成很多模块。其次,groupId不应该对应项目隶属的组织或公司。原因很简单,一个组织下会有很多实际项目,如果groupId只定义到组织级别,而后面我们会看到,artifactId只能定义Maven项目(模块),那么实际项目这个层次将难以定义。最后,groupId的表示方式与Java包名的表达方式类似,通常与域名反向一一对应。
artifactId : 该元素定义当前实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId的前缀。比如上例中的my-app。
version : 该元素定义Maven项目当前的版本
packaging :定义Maven项目打包的方式,首先,打包方式通常与所生成构件的文件扩展名对应,如上例中的packaging为jar,最终的文件名为my-app-0.0.1-SNAPSHOT.jar。也可以打包成war, ear等。当不定义packaging的时候,Maven 会使用默认值jar
classifier: 该元素用来帮助定义构建输出的一些附件。附属构件与主构件对应,如上例中的主构件为my-app-0.0.1-SNAPSHOT.jar,该项目可能还会通过一些插件生成如my-app-0.0.1-SNAPSHOT-javadoc.jar,my-app-0.0.1-SNAPSHOT-sources.jar, 这样附属构件也就拥有了自己唯一的坐标
依赖范围
Maven有以下几种依赖范围:
- compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。典型的例子是spring-core,在编译,测试和运行的时候都需要使用该依赖。
- provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。
- test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子就是JUnit,它只有在编译测试代码及运行测试的时候才需要。
- runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
- system:系统依赖范围。该依赖范围与provided所表示的依赖范围一致,对于编译和测试classpath有效,但在运行时无效。只是使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用,systemPath元素可以引用环境变量
上述除import以外的各种依赖范围与三种classpath的关系如下:
maven之快照版本
maven 快照(2.1-Snapshots)形式的存在理由:方便未完整的功能代码发布,A开发,B能及时获取得到最新的代码。
Maven的快照版本机制就是为了解决上述问题。在该例中,小张只需要将模块A的版本设定为2.1-SNAPSHOT,然后发布到私服中,在发布的过程中,Maven会自动为构件打上时间戳。比如:2.1-20091214.221414-13就表示2009年12月14日 22点14分14秒的第13次快照。有了该时间戳,Maven就能随时找到仓库中该构件2.1-SNAPSHOT版本最新的文件。这时,季MM配置对于模块A的2.1-SNAPSHOT版本的依赖,当她构件模块B的时候,Maven会自动从仓库中检查模块A的2.1-SNAPSHOT的最新构件,当发现有更新时便进行下载。默认情况下,Maven每天检查一次更新(由仓库配置的updatePolicy控制),用户也可以使用命令行-U参数强制让Maven检查更新,如:mvn clean install-U。
基于快照版本机制,小张在构建成功之后才能将构件部署至仓库,而季MM可以完全不用考虑模块A的构建,并且她能确保随时得到模块A的最新可用的快照构件,而这一切都不需要额外的手工操作。
生命周期和插件
项目的清理,初始化,编译,测试,打包,集成测试,验证 部署,和站点生成。
在这个阶段中,比较重要和常用的阶段有:
validate
generate-sources
process-sources
generate-resources
process-resources:复制并处理资源文件至目标目录,准备打包
compile:编译项目源代码
process-clases
generate-test-sources
procss-test-sources
generate-test-resources
process-test-resources:复制并处理资源文件至目标测试目录
test-compile:编译测试源代码
process-test-classes
test:使用合适的单元测试框架测试运行,这些测试代码将不会被打包或部署
prepare-package
package:接受编译好的代码,打包成可发布的格式,如jar
pre-integration-test
integration-test
post-integration-test
verify
install:将包安装至本地仓库,以便让其它项目依赖
deploy:将最终的包复制到远程仓库,以便让其它开发人员与项目共享
基本上,只要根据名称我们就可以猜测出每个阶段的用途。要记住的是,任何一个阶段的时候,它前面的所有阶段都会被运行,这也就是为什么我们运行mvn clean install的时候,代码会被编译、测试、打包。
mvn clean 中的clean就是上面的clean,在一个生命周期中,运行某个阶段的时候,它之前的所有阶段都会被运行,也就是说,mvn clean 等同于 mvn pre-clean clean ,如果我们运行 mvn post-clean ,那么 pre-clean,clean 都会被运行。这是Maven很重要的一个规则,可以大大简化命令行的输入。
常用插件介绍:
- maven-antrun-plugin
- maven-archetype-plugin
- maven-assembly-plugin
- maven-dependency-plugin
- maven-enforcer-plugin
- maven-help-plugin
- maven-release-plugin
- maven-resources-plugin
…..
聚合与继承
聚合:就是用一个总的maven项目包含其他子maven项目,这样,就不需要一个个打包运行mvn命令了,也可以方便继承的体现,优化pom.xml的重复项。
继承:最好的方式是使用dependencyManagement 可以免去一些配置.
同理,插件管理 也有pluginManagement,具体使用细则省略。
参考
Java构建工具:Ant vs Maven vs Gradle