Maven基础总结
鉴于最近基本看完《Maven实战》这本书,对于我自己的所看的结果作一下总结,理清自己的思路,并复习书中的知识。当然有时间会继续学习一下Gradle,似乎是一个更好的工具。
我原来对于Maven的印象就是依赖管理的工具,但是在认真学习之后,认识到Maven可以实现挺多实用功能。
- 自动化构建
- 依赖管理(提供中央仓库,能够帮我们自动下载构建)
- 项目信息管理
在Maven中最重要的思维莫过于约定优于配置。虽然在Maven中没有确定的文件定义一些要求,但是大家约定的一些写法等,保证了项目的移植性,当然也可以自定义,但是不推荐(因为你写了可能就自己看得懂了,别人都看不懂)。在Maven项目中默认的主代码目录为src/test/java
,默认的测试代码目录为src/test/java
。
Pom文件
在平时开发中,感觉到pom.xml文件是Maven项目中最重要的一环,它提供了项目信息与依赖管理等。
首先,pom.xml文件中,包含一般XML文件头,指定xml文件版本以及编码方式等;接下来是project元素,包含相关的命名空间以及xsd元素等。
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
元素
接下来重要的是groupId
、artifactId
、version
元素,分别表示组(当前Maven项目隶属的实际项目)、唯一ID、版本。这个是Maven的坐标元素,基本可以确定一个项目,当然这三项是必须的,不管是在项目信息还是在依赖管理中,还有packaging
、classifier
分别表示打包方式和帮助定义构建输出的一些附属构建( classifier
是不能直接定义的,附属构建不是项目直接默认生成的,而是由附加的插件帮助生成)。例如在本例中因为是Javaweb项目,所以使用war的打包方式,在项目中如果不做声明,默认的是jar打包方式。这5个元素可以唯一的确定项目。
版本
关于版本,分为发布版本和快照版本,在本例中的1.0-SNAPSHOT
就是快照版本,快照版本是不稳定的。在Maven中版本号的约定是<主版本>.<次版本>.<增量版本>-<里程碑版本>。关于版本管理的一些本文不会涉及。
在配置的pom文件中提供了properties标签自定义。利用这个我们将所有的版本号集中在一起,方便更新、引用,以及减少一些版本号重复性。
依赖
除了在项目信息中使用到了这些元素标签,还在依赖管理中使用,这是很必要的,需要用它们去确定一个Maven项目。在 dependencies
里会有许多dependency
来确定每个依赖。例如spring项目中一般会包含spring-core
、spring-context
、spring-context-support
等都是Spring Framework实现依赖注入等功能必要的构建,都需要在项目中依赖。
在依赖的servlet
中定义了scop标签,表示定义依赖的范围,那么provided是什么意思呢?在一般情况中,我们有6种依赖范围:1、compile:编译依赖范围,一般在缺省默认情况下也使用这个默认范围;2、test:测试依赖范围;3、provided:已提供依赖范围,表示对于编译和测试classpath有效,但是在运行的时候无效;4、runtime:运行时依赖范围,即测试和运行classpath有效;5、system:系统依赖范围,该依赖范围与三种classpath的关系与provided相同,但是在使用这个依赖范围时,必须通过systemPath元素显式地指定依赖文件的路径,在使用时会造成不可移植性;6、import:导入依赖范围,其实是继承父模版的依赖配置,继承依赖范围。
依赖范围(scop) | 对于编译classpath有效 | 对于测试classpath有效 | 对于运行classpath有效 | 示例 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | - | Y | - | JUnit |
provided | Y | Y | - | servlet-api |
runtime | - | Y | Y | JDBC驱动实现 |
system | Y | Y | - | |
类似于java的属性继承一样,Maven也具有传递性依赖,继承依赖范围关系如下,左一列为直接依赖,横一栏为间接依赖,内容表示最终依赖范围。 |
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
这儿其实有一些规律:在间接依赖为compile时其他两者一致;当间接依赖为test时,不具有传递性;当间接依赖为provided时,只有provided才能传递,且最终依赖为provided;当间接依赖为runtime时,一般情况时直接依赖与最终依赖一致,除了直接依赖为compile时最终依赖为runtime。 |
既然有传递性依赖,以及继承等机制(这些会在后续讲到),并没有像Java一样限制只能单继承,那么必会出现像C++一样通过不同路径继承同意文件而产生冲突的情况,那么如何解决?Mave这儿需要依赖调解。第一原则是路径最近者优先;第二原则是第一声明者优先。
当然在依赖的时候,提供了optional标签来表示可选。可选依赖是不会传递的。也提供exclusions标签来排除继承时的某些依赖,可以解决在继承时快照版本依赖的不稳定性问题。
仓库
在上文中讲到依赖,那么依赖后引入的包相对于其对应仓库中的路径应该是多少呢?在路径与坐标的大致对应关系为groupId/artifactId/version/artifactId-version.packaging
。
对于Maven来说,仓库只分为两种:本地仓库和远程仓库。
一般情况是当我们使用依赖去引入一种构建,当Maven根据坐标寻找构建时,先会去本地查找此构建,如果本地没有这个构建,去远程仓库查找,发现则下载到本地使用(当然本地构建需要查看更新时也是需要去远程仓库查找)。
本地仓库
一般在用户中本机中,默认情况下都有一个.m2/repository/
的仓库目录。
远程仓库
远程仓库分为中央仓库以及自己建立的私服等。一般情况下,基本每个公司都是有自己的Maven仓库的,在开发之前的环境配置时会加上一个自己公司的setting.xml文件的配置。
生命周期
Maven拥有三套独立的生命周期,分别为clean、default、site。clean生命周期目标是清理项目;default是构建项目;而site生命周期目的是建立项目站点。
clean生命周期包括pre-clean、clean、post-clean。一般调用clean时会依次执行pre-clean、clean。一般命令都是执行到指定的阶段截止。
default是所有生命周期中最核心的部分。包括了许多阶段:calidate、initialize、generate-sources、process-sources、generate-resources、process-resources、compile、process-classes、generate-test-sources、process-test-sources、generate-test-resources、process-test-resources、test-compile、process-test-clasess、test、prepare-package、package、pre-integration-test、integration-test、post-integration-test、verify、install、deploy。在这个生命周期中可以看到有许多编译、测试等阶段。
而site生命周期有pre-site、site、post-site、site-deploy四个阶段。
插件
个人觉得Maven中插件是非常重要的一环,可以帮助我们完成一些任务,并且与生命周期中的某个阶段绑定。
1 | ... |
在这段代码中使用plugin标签加入了maven-compiler-plugin以及tomcat插件,可以使得项目可编译以及不用本地的tomcat服务器。compiler的插件是内置绑定的compile阶段,不用显式申明。当然也可以自定义绑定,在配置中加入executions、execution标签配置 执行一个任务,并用phase绑定生命周期。
测试
讲到插件,就不能跳过Maven的测试。测试也是使用插件来实现的,如maven-surefire-plugin插件。可以帮助我们单元测试、集成测试等。
聚合与继承
聚合特性能把项目的各个模块聚合在一起构建,而继承特性能帮助抽取各模块相同的依赖和插件等配置。
所有模块组成的一个构建结构就是反应堆。单模块项目就是这个模块本身;而多模块项目则包含了各模块之间的继承和依赖关系、计算合理构建顺序。
附
本文中对于许多详细知识没有作总结,只是对于常用的一些部分作了浅入的涉及。如测试、聚合与继承、以及Nexus建私服、profile、站点等知识没有解释,需要学习的可以仔细看一下《Maven实战》。