Maven 是 Apache 组织下的一个跨平台的项目管理工具,主要服务于基于Java平台的项目构建,项目管理和项目信息管理,提供了帮助管理构建、文档、报告、依赖、scms、发布、分发的方法。可以方便的编译代码、进行依赖管理、管理二进制库等等。Maven 提供了标准的软件生命周期模型和构建模型,通过配置就能对项目进行全面的管理。它的跨平台性保证了在不同的操作系统上可以使用相同的命令来完成相应的任务。Maven 将构建的过程抽象成一个个的生命周期过程,在不同的阶段使用不同的已实现插件来完成相应的实际工作,这种设计方法极大的避免了设计和脚本编码的重复,极大的实现了复用。
pom全称为Project Object Model, 简单说就是要对构建的项目进行建模,将要构建的项目看成是一个对象Object,这个对象就会有相应的属性,属性在maven中用坐标表示,坐标元素包括groupId、artifactId、version、packaging、classfier。
元素 | 描述 | ext |
---|---|---|
groupId | 定义当前模块隶属的实际Maven项目, 表示方式与Java包类似 | groupId不应直接对应项目隶属的公司/组织(一个公司/组织下可能会有很多的项目) |
artifactId | 定义实际项目中的一个Maven模块 | 推荐使用项目名作为artifactId前缀, 因为Maven打包默认以artifactId作为前缀 |
version | 定义当前项目所处版本(如1.0-SNAPSHOT、4.2.7.RELEASE、1.2.15、14.0.1-h-3 等) | Maven版本号定义约定: <主版本>.<次版本>.<增量版本>-<里程碑版本> |
packaging | 定义Maven项目打包方式, 通常打包方式与所生成构件扩展名对应 | 有jar(默认)、war、pom、maven-plugin等 |
classifier | 用来帮助定义构建输出的一些附属构件(如javadoc、sources) | 不能直接定义项目的classifier(因为附属构件不是由项目默认生成, 须有附加插件的帮助) |
Maven最著名的(也是几乎每个人最先接触到的)就是Maven的依赖管理, 它使得我们不必再到开源项目的官网一个个下载开源依赖包, 然后再一个个放入classpath. 一个依赖声明可以包含如下元素:
元素 | 描述 | ext |
---|---|---|
groupId、artifactId和version | 依赖的基本坐标(最最重要) | |
type | 依赖的类型, 对应于项目坐标定义的packaging | 默认jar |
scope | 依赖的范围, 用来控制依赖与三种classpath(编译classpath、测试classpath、运行classpath)的关系 | 包含compile、provided、runtime、test、system和import 6种 |
optional | 依赖是否可选 | 假如一个Jar包支持MySQL与Oracle两种DB, 因此其构建时必须添加两类驱动, 但用户使用时只会选择一种DB. 此时对A、B就可使用optional可选依赖 |
exclusions | 排除传递性依赖 | 传递性依赖极大的简化了项目依赖的管理, 但也会引入Jar包版本冲突等问题, 此时就需要对传递性依赖进行排除 |
重点注意
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
若项目中多个Jar同时引用了相同的Jar时,会产生依赖冲突,但Maven采用了两种避免冲突的策略,因此在Maven中是不存在依赖冲突的。
其中依赖范围scope用来控制依赖和编译,测试,运行的classpath的关系,主要的是四种依赖范围如下:
Snapshot版本代表不稳定、尚处于开发中的版本 ;
Release版本则代表稳定的版本 。
1、什么情况下该用SNAPSHOT?
协同开发时,如果A依赖构件B,由于B会更新,B应该使用SNAPSHOT来标识自己
2、不用Release版本,在所有地方都用SNAPSHOT版本行不行?
不行,正式环境中不得使用snapshot版本的库。 比如说,今天你依赖某个snapshot版本的第三方库成功构建了自己的应用,明天再构建时可能就会失败,因为今晚第三方可能已经更新了它的snapshot库。你再次构建时,Maven会去远程repository下载snapshot的最新版本,你构建时用的库就是新的jar文件了,这时正确性就很难保证了。稳定版应该依赖Release版本的jar包,这样更新时需要更新版本号,版本号没变时,依赖的jar包也不会变。
项目的构建过程对应的是PO对象的build属性,对应pom.xml中也就是元素中的内容。
在maven中一个构建过程就对应一个Lifecycle,即生命周期。这个Lifecycle也分为多个阶段,每个阶段叫做phase。一个标准的构建Lifecycle包含了如下的phase:
在maven中,你执行任何一个phase时,maven会将其之前的phase都执行。例如 mvn install,那么maven会将deploy之外的所有phase按照他们出现的顺序一次执行。
上面Lifecycle的定义,也就是说maven为程序的构建定义了一套规范流程:第一步需要validate,第二步需要initialize… … compile,test,package,… … install,deploy,但是并没有定义每一个phase具体应该如何操作。这里的phase的作用有点类似于Java语言中的接口,只协商了一个契约,但并没有定义具体的动作。比如说compile这个phase定义了在构建流程中需要经过编译这个阶段,但没有定义应该怎么编译(编译的输入是什么?用什么编译javac/gcc?)。这里具体的动作就是由goal来定义,一个goal在maven中就是一个Mojo(Maven old java object)。Mojo抽象类中定义了一个execute()方法,一个goal的具体动作就是在execute()方法中实现。实现的Mojo类应该放在哪里呢?答案是maven plugin里,所谓的plugin其实也就是一个maven项目,只不过这个项目会引用maven的一些API,plugin项目也具备maven坐标。
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-dependency-pluginartifactId>
<executions>
<execution>
<id>copy-dependenciesid>
<phase>prepare-packagephase>
<goals>
<goal>copy-dependenciesgoal>
goals>
<configuration>
<outputDirectory>${project.basedir}/libsoutputDirectory>
<overWriteReleases>falseoverWriteReleases>
<overWriteSnapshots>falseoverWriteSnapshots>
<overWriteIfNewer>trueoverWriteIfNewer>
configuration>
execution>
executions>
plugin>
plugins>
build>
在执行具体的构建时,我们需要为lifecycle的每个phase都绑定一个goal,这样才能够在每个步骤执行一些具体的动作。比如在lifecycle中有个compile phase规定了构建的流程需要经过编译这个步骤,而maven-compile-plugin这个plugin有个compile goal就是用javac来将源文件编译为class文件的,我们需要做的就是将compile这个phase和maven-compile-plugin中的compile这个goal进行绑定,这样就可以实现Java源代码的编译了。那么有人就会问,在哪里绑定呢?答案是在pom.xml元素中配置即可。
就将maven-dependency-plugin中的copy-dependencies这个goal绑定到了prepare-package这个phase,后续在maven执行到prepare-package phase时就会执行copy-dependencies goal。
Maven仓库可简单分成两类: 本地仓库与远程仓库. 当Maven根据坐标寻找构件时, 它会首先检索本地仓库, 如果本地存在则直接使用, 否则去远程仓库下载.
私服是一种特殊的远程仓库, 它设在局域网内, 通过代理广域网上的远程仓库, 供局域网内的Maven用户使用.
当需要下载构件时, Maven客户端先向私服请求, 如果私服不存在该构件, 则从外部的远程仓库下载, 并缓存在私服上, 再为客户提供下载服务. 此外, 一些无法从外部仓库下载到的构建也能从本地上传到私服供大家使用(如公司内部二方包、Oracle的JDBC启动等). 为了节省带宽和时间, 一般在公司内部都会架设一台Maven私服, 但将公司内部项目部署到私服还需要对POM做如下配置:
<project >
...
<distributionManagement>
<repository>
<id>releasesid>
<url>http://mvn.server.com/nexus/content/repositories/releases/url>
repository>
<snapshotRepository>
<id>snapshotsid>
<url>http://mvn.server.com/nexus/content/repositories/snapshots/url>
snapshotRepository>
distributionManagement>
project>