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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>com.fei</groupId>
<artifactId>fei.empty.spring.web</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<properties>
<jetty.port>8417</jetty.port>
<spring.version>4.2.0.RELEASE</spring.version>
<mybatis.version>3.3.1</mybatis.version>
<slf4j.version>2.6.2</slf4j.version>
<log4j2.version>2.6.2</log4j2.version>
</properties>
<dependencyManagement>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
...

元素

接下来重要的是groupIdartifactIdversion元素,分别表示组(当前Maven项目隶属的实际项目)、唯一ID、版本。这个是Maven的坐标元素,基本可以确定一个项目,当然这三项是必须的,不管是在项目信息还是在依赖管理中,还有packagingclassifier分别表示打包方式和帮助定义构建输出的一些附属构建( classifier是不能直接定义的,附属构建不是项目直接默认生成的,而是由附加的插件帮助生成)。例如在本例中因为是Javaweb项目,所以使用war的打包方式,在项目中如果不做声明,默认的是jar打包方式。这5个元素可以唯一的确定项目。

版本

关于版本,分为发布版本和快照版本,在本例中的1.0-SNAPSHOT就是快照版本,快照版本是不稳定的。在Maven中版本号的约定是<主版本>.<次版本>.<增量版本>-<里程碑版本>。关于版本管理的一些本文不会涉及。

在配置的pom文件中提供了properties标签自定义。利用这个我们将所有的版本号集中在一起,方便更新、引用,以及减少一些版本号重复性。

依赖

除了在项目信息中使用到了这些元素标签,还在依赖管理中使用,这是很必要的,需要用它们去确定一个Maven项目。在 dependencies里会有许多dependency来确定每个依赖。例如spring项目中一般会包含spring-corespring-contextspring-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有效示例
compileYYYspring-core
test-Y-JUnit
providedYY-servlet-api
runtime-YYJDBC驱动实现
systemYY-
类似于java的属性继承一样,Maven也具有传递性依赖,继承依赖范围关系如下,左一列为直接依赖,横一栏为间接依赖,内容表示最终依赖范围。
compiletestprovidedruntime
compilecompile--runtime
testtest--test
providedprovided-providedprovided
runtimeruntime--runtime
这儿其实有一些规律:在间接依赖为compile时其他两者一致;当间接依赖为test时,不具有传递性;当间接依赖为provided时,只有provided才能传递,且最终依赖为provided;当间接依赖为runtime时,一般情况时直接依赖与最终依赖一致,除了直接依赖为compile时最终依赖为runtime。

既然有传递性依赖,以及继承等机制(这些会在后续讲到),并没有像Java一样限制只能单继承,那么必会出现像C++一样通过不同路径继承同意文件而产生冲突的情况,那么如何解决?Mave这儿需要依赖调解。第一原则是路径最近者优先;第二原则是第一声明者优先。

当然在依赖的时候,提供了optional标签来表示可选。可选依赖是不会传递的。也提供exclusions标签来排除继承时的某些依赖,可以解决在继承时快照版本依赖的不稳定性问题。

仓库

在上文中讲到依赖,那么依赖后引入的包相对于其对应仓库中的路径应该是多少呢?在路径与坐标的大致对应关系为groupId/artifactId/version/artifactId-version.packaging

对于Maven来说,仓库只分为两种:本地仓库和远程仓库。

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>${web.port}</port>
<path>/${project.artifactId}</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
...

在这段代码中使用plugin标签加入了maven-compiler-plugin以及tomcat插件,可以使得项目可编译以及不用本地的tomcat服务器。compiler的插件是内置绑定的compile阶段,不用显式申明。当然也可以自定义绑定,在配置中加入executions、execution标签配置 执行一个任务,并用phase绑定生命周期。

测试

讲到插件,就不能跳过Maven的测试。测试也是使用插件来实现的,如maven-surefire-plugin插件。可以帮助我们单元测试、集成测试等。

聚合与继承

聚合特性能把项目的各个模块聚合在一起构建,而继承特性能帮助抽取各模块相同的依赖和插件等配置。

所有模块组成的一个构建结构就是反应堆。单模块项目就是这个模块本身;而多模块项目则包含了各模块之间的继承和依赖关系、计算合理构建顺序。

本文中对于许多详细知识没有作总结,只是对于常用的一些部分作了浅入的涉及。如测试、聚合与继承、以及Nexus建私服、profile、站点等知识没有解释,需要学习的可以仔细看一下《Maven实战》。