Maven坐标为各种构建引入了秩序,任何一个构建都必须明确自己的坐标,而一组Maven坐标是通过一些元素定义的,他们是groupId, artifactId, version, packaging, classifer。
- groupId: 定义当前Maven项目隶属的实际项目
- 首先:Maven项目和实际项目不一定是一一对应的关系;
- 其次:groupId不应该对应项目隶属的组织或公司;
- 最后:groupId的表示方式和Java包名的表示方式类似,通常与域名反向一一对应。
- artifactId: 定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId的前缀
- version: 定义Maven项目当前所处的版本
- packaging: 定义Maven项目的打包方式
- 首先: 打包方式通常与所生成构件的文件扩展名对应
- 其次:打包方式会影响到构建的生命周期
- 最后: 不定义packaging,默认使用jar
- classifer: 帮助定义构件输出的一些附属构件
Example: pom.xml
<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">
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.9.RELEASEversion>
<relativePath/>
parent>
<groupId>com.tm.sbiagroupId>
<artifactId>sbia-cart-webartifactId>
<version>1.0-SNAPSHOTversion>
<modelVersion>4.0.0modelVersion>
<name>sbia-cart-webname>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
<distributionManagement>
<repository>
<id>toman.releasesid>
<name>Toman maven2 repository-releasesname>
<url>http://artifactory.toman.com/libs-releases-localurl>
repository>
<snapshotRepository>
<id>toman.snapshotsid>
<name>Toman maven2 repository-snapshotsname>
<url>http://artifactory.toman.com/libs-snapshots-localurl>
snapshotRepository>
distributionManagement>
project>
根元素project下的dependencies可以包含一个或多个dependency元素,以声明一个或多个依赖项目。每个依赖都可以包含的元素有:
- groupId, artifactId, version: 依赖的基本坐标
- type: 依赖的类型,对应于坐标定义的packaging。大部分情况下该元素无需声明,默认为jar
- scope: 依赖的范围
- optional: 标记依赖是否可选
- exclusions: 用来排除传递性依赖
依赖范围用来控制依赖与三种classpath(编译classpath, 测试classpath, 运行classpath)的关系
依赖范围(Scope) | 编译classpath有效 | 测试classpath有效 | 运行classpath有效 | 例子 |
---|---|---|---|---|
complie | Y | Y | Y | spring-core |
test | – | Y | – | Junit |
provided | Y | Y | – | servlet-api |
runtime | – | Y | Y | JDBC驱动实现 |
system | Y | Y | – | 本地的,Maven仓库之外的类库文件 |
依赖范围不仅可以控制依赖和三种classpath的关系,还对传递性依赖产生影响
如下图,最左侧一列表示第一直接依赖范围,最上面一行标识第二直接依赖范围,中间交叉的单元格则表示传递性依赖范围
第一直接依赖范围(列)/ 第二直接依赖范围(行) | 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。
假设有这样一个依赖关系,项目A依赖于项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖:A–>B、B–>X(可选)、B–>Y(可选)。根据传递性依赖的定义,如果所有这三个依赖的范围都是compile,那么X、Y就是A的compile范围传递依赖。然而由于X、Y是可选依赖、依赖将不会得以传递。
为什么要使用可选依赖这一特征呢?可能项目B实现了两个特征,其中的特征一依赖于X,特征二依赖于Y,而且这两个特征是互斥的,用户不能同时使用两个特性。比如B是一个持久层隔离工具包支持多种数据库,包括MySQL、PostgreSQL等,在构建这个工具包的时候,需要这两种数据库的驱动程序,而在使用这个工具包的时候,只会依赖一个数据库。
理想情况下是不应该使用可选依赖的,使用可选依赖的原因是某一个项目实现了多个特性,而这是不符合单一职责原则的。
Maven依赖调解的第一原则:路径最近者优先;
A -> B -> C -> X(1.0)
A -> D -> X(2.0)
依据第一原则 X的版本为2.0
Maven依赖调解的第二原则:第一声明者优先;
A -> B -> Y(1.0)
A -> C -> Y(2.0)
B的依赖声明在C之前
依据第二原则Y的版本为1.0
得益于坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。
任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这便是Maven的仓库布局方式。
对于Maven来说仓库只分为两类:本地仓库和远程仓库。当Maven根据坐标寻找构件的时候,首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件后,下载到本地仓库再使用。
中央仓库是Maven自带的远程仓库,包含了绝大部分开源的构件。在默认配置下,当本地仓库没有Maven需要的构件的时候,他就会尝试从中央仓库下载。
私服是另一种特殊的远程仓库,为了节省宽带和时间,应该在局域网内架设一个私有的仓库服务器,用其代理所有的外部远程仓库。内部的项目还能部署到私服上供其他项目使用。
对于release和snapshots来说,除了enabled, 他们还包含另外两个子元素updatePolicy和checksumPolicy。
updatePolicy用来配置Maven仓库从远程仓库检查更新的频率,可选值:
- daily - 每天检查一次,默认值
- never - 从不检查更新
- always - 每次构件都检查更新
- interval: X - 每隔X分钟检查一次更新。X为任意整数
元素checksumPolicy用来配置Maven检查检验和文件的策略。当构件被部署到Maven仓库时,会同时部署对应的校验和文件。checksumPolicy可选值:
- warn: 执行构建时输出警告信息。默认值
- fail: 遇到校验和错误就让构建失败
- ignore: 使Maven完全忽略校验和错误
Maven自带的中央仓库使用的id为central,如果其他的仓库声明也使用该id,就会覆盖中央仓库的配置。
<settings
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"
xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<localRepository>/maven/repository/locallocalRepository>
<interactiveMode>trueinteractiveMode>
<offline>falseoffline>
<profiles>
<profile>
<id>tomanRepositoryid>
<repositories>
<repository>
<snapshots>
<enabled>falseenabled>
snapshots>
<id>centralid>
<name>libs-releasesname>
<url>http://artifactory.toman.com/libs-releasesurl>
repository>
<repository>
<snapshots/>
<id>snapshotsid>
<name>libs-snapshotsname>
<useUniqueVersions>falseuseUniqueVersions>
<url>http://artifactory.toman.com/libs-snapshotsurl>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>falseenabled>
snapshots>
<id>centralid>
<name>plugins-releasesname>
<url>http://artifactory.toman.com/plugins-releasesurl>
pluginRepository>
<pluginRepository>
<snapshots/>
<id>snapshotsid>
<name>plugins-snapshotsname>
<useUniqueVersions>falseuseUniqueVersions>
<url>http://artifactory.toman.com/plugins-snapshotsurl>
pluginRepository>
pluginRepositories>
profile>
profiles>
<servers>
<server>
<id>toman.releasesid>
<username>tomanusername>
<password>toman's passwordpassword>
server>
<server>
<id>toman.snapshotsid>
<username>tomanusername>
<password>toman's passwordpassword>
server>
servers>
<activeProfiles>
<activeProfile>tomanRepositoryactiveProfile>
activeProfiles>
settings>
快照(SNAPSHOT)是一种特殊的版本,指定了某个当前的开发进度的副本。主要解决开发过程中频繁构建的问题,当项目经过完善的测试需要发布的时候,就应该将快照版本更改为发布版本。
当本地仓库没有依赖构件的时候,Maven会自动从远程仓库下载;当依赖版本为快照版本的时候,Maven会自动找到最新的快照。其背后的解析机制如下:
- 当依赖的范围为system的时候,Maven直接从本地文件系统解析构件;
- 根据依赖坐标计算仓库路径后,尝试从本地仓库寻找构件,如果发现相应的构件,则解析成功;
- 在本地仓库不存在相应构件的情况下,如果依赖的版本是显式的发布版本构件,则遍历所有的远程仓库,发现后下载并解析使用;
- 如果依赖的版本是RELEASE或LASTEST,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/maven-metadata.xml,将其与本地仓库对应的元数据合并后,计算出RELEASE或LASTEST的真实值,然后基于这个真实值检查本地和远程仓库,如步骤2,3;
- 如果依赖的版本是SNAPSHOT,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/version/maven-metadata.xml,将其与本地仓库对应的元数据合并后,得到最新快照版本的值,然后基于该值检查本地仓库,或者从远程仓库下载;
- 如果最后解析得到的构件版本是时间戳格式的快照,则复制其时间戳格式的文件至非时间戳格式,如SNAPSHOT,并使用该非时间戳格式的构件。
在依赖中使用LATEST和RELEASE是不推荐的做法。
如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。
Maven实战(Maven In Action)
Maven快照机制(SNAPSHOT)