(五)Maven仓库
Maven仓库
在Maven世界中,任何一个依赖、插件或者项目构建的输出,都可以成为构件(jar、war包等)。
Maven在某个位置上统一存储所有Maven项目共享的构件,即为Maven仓库;
Maven项目不用各自存储依赖文件,只需声明依赖的坐标,在需要时(编译、运行、打包),Maven会自动根据坐标找到Maven仓库中的构件,并使用它们。
仓库的布局
在Maven世界中,任何一个构件都有其唯一的坐标,通过坐标可以定义其在仓库中的唯一存储路径,即Maven的仓库布局方式。
Maven构件的存储路径为:groupId/artifactId/version/artifactId-version[-classifier].packaging
Ps(个人理解): 仓库的布局方式就是定义了坐标与构件路径的对应关系,即如何通过坐标找到构件。
仓库的分类
对Maven而言,仓库只分为两类,本地仓库、远程仓库。而远程仓库又可细分为中央仓库、私服、其他公共库。
当Maven根据坐标寻找构件时,首先查看本地仓库,如果不存在,Maven就会去远程仓库查找并下载到本地文件。如果都没有,Maven就会报错。分类如下图
1 本地仓库
通常Maven项目目录下,没有诸如lib/这样存放依赖文件的目录,Maven总是基于坐标使用本地仓库的依赖文件。
默认情况下,不论是Windows还是Linux,本地仓库的路径都为~/.m2/repository(~ 是用户目录)
更改本地仓库路径:~/.m2/settings.xml,设置localRepository元素的值。
如:
<settings>
...
<localRepository>D:\java\repository</localRepository>
...
</settings>
安装构件到本地仓库:
1)mvn clean install:构建项目输出文件并安装到本地仓库
2)mvn install:install-file -Dfile=x -DgroupId=x -DartifactId=x -Dversion=x -Dpackaging=x
Dfile待安装构建路径;DgroupId、DartifactId、Dversion、Dpackaging声明其在仓库中的坐标
2 远程仓库
安装好Maven后,如果不执行任何Maven命令,本地仓库目录是不存在的。当用户输入第一条Maven命令之后,Maven才会创建本地仓库,然后根据配置和需要,从远程仓库下载构件至本地仓库。
Maven本地仓库只有一个,但可以配置多个远程仓库。
好比本地仓库是书房,远程仓库是书店,当用户需要看一本书时,如果在书房中没找到,就到书店购买并放回书房。通常一个人只有一个书房,但外面的书店有很多。
3 中央仓库
由于原始的本地仓库是空,Maven必须知道至少一个可用的远程仓库,才能在执行Maven命令时下载到需要的构件。
中央仓库就是这样一个默认远程仓库。在Maven安装文件中自带了中央仓库的配置。
在Maven安装目录/lib/maven-model-builder-xx.jar中访问路径org/apache/maven/model/pom-4.0.0.xml。
Ps(个人理解):pom-4.0.0.xml 与 pom.xml中<modelVersion>4.0.0</modelVersion> 应该存在某些关联
pom-4.0.0.xml是所有Maven项目都会继承的超级POM,查看该文件,可以找到中央仓库配置。
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
4 私服
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务。私服代理广域网上的远程仓库,供局域网内的Maven用户使用。
上图展示了组织内部使用私服的情况,即使在一台直接连入Internet的个人机器上使用Maven,也应本地建立私服。
建立私服的优点:
节省自己的外网带宽 | 消除对外的重复构件下载,降低外网带宽的压力。 |
加速Maven构建 | 不停的请求外部仓库十分耗时,Maven的一些内部机制(如快照更新检查)要求Maven在执行构建时不停地检查远程仓库数据,当项目配置很多外部远程仓库时,构建速度大大降低。使用私服,Maven只需检查局域网内私服数据,提高构建速度。 |
部署第三方构建 | 安装组织内部生成的私有构件到私服,供内部Maven用户 |
提高稳定性,增强控制 | Maven构建高度依赖于远程仓库,Internet不稳定,Maven构建也会变得不稳定,甚至失败,使用私服后,即使暂时没有Internet,由于私服缓存了很多构件,Maven也能正常运行。此外,很多私服(如Nexus)软件还提供了额外的功能,如权限管理,RELEASE/SNAPSHOT区分等 |
降低中央仓库的负荷 | 使用私服可以避免很多对中央仓库的重复下载。 |
远程仓库的配置
在POM中配置远程仓库
<repositories>
<repository>
<id>...</id> <!-- 仓库唯一标识,重复会覆盖上一个远程仓库 -->
<name>...</name> <!-- 仓库名称 -->
<url>...</url> <!-- 仓库地址 -->
<releases> <!-- 重要!控制Maven对于发布版本构件的下载 -->
<enabled>...</enabled> <!-- true/false 控制发布版本构件的下载 -->
<updatePolicy>...</updatePolicy> <!-- 更新策略 daily(默认,每天一次)、never(从不)、always(每次构建)、interval:X(间隔X分钟) -->
<checksumPolicy>...</checksumPolicy> <!-- 检查检验和文件的策略 warn(默认,校验失败,输出警告信息)、fail(校验失败,无法完成构建)、ignore(忽略校验失败) -->
</releases>
<snapshots> <!-- 重要!控制Maven对于快照版本构件的下载 -->
<enabled>...</enabled> <!-- true/false 控制快照版本构件的下载 -->
<updatePolicy>...</updatePolicy> <!-- 更新策略 daily(默认,每天一次)、never(从不)、always(每次构建)、interval:X(间隔X分钟) -->
<checksumPolicy>...</checksumPolicy> <!-- 检查检验和文件的策略 warn(默认,校验失败,输出警告信息)、fail(校验失败,无法完成构建)、ignore(忽略校验失败) -->
</snapshots>
<layout>default</layout> <!-- 仓库布局方式 -->
</repository>
...
</repositories>
1 远程仓库的认证
大部分远程仓库无须认证就可访问,但有时出于安全方面考虑,我们需要提供认证信息才能访问一些远程仓库。
配置认证信息必须配置在settings.xml中,因为POM往往提交到代码仓库中供所有成员访问,显然,本地settings.xml更安全
<settings>
...
<servers>
<server>
<id>..</id> <!-- 需要提供认证信息才能访问的远程仓库ID -->
<username>...</username> <!-- 用户名 -->
<password>...</password> <!-- 密码 -->
</server>
</servers>
...
</settings>
2 部署至远程仓库
1) 编辑项目的pom.xml
<project>
<distributionManagement>
<repository> <!-- 指定发布版本构件的仓库 -->
<id>...</id>
<name>...</name>
<url>...</url>
</repository>
<snapshotRepository> <!-- 指定快照版本构件的仓库 -->
<id>...</id>
<name>...</name>
<url>...</url>
</snapshotRepository>
</distyibutionManagement>
</project>
2) 往远程仓库部署构件时,往往需要认证。在settings.xml中配置认证信息
3)执行mvn clean deploy,部署到远程仓库
快照版本
在Maven世界中。任何一个项目或者构件都必须有自己的版本,而Maven又将版本分为两种:
发布版本:稳定,格式如:1.0.0、1.3-alpha-4、2.0
快照版本:不稳定,格式如:2.1-SNAPSHOT、2.1-20091214.221414-13
假设:同时开发项目A、B,而项目B依赖于项目A,在项目未正式方布时,开发方案如下
方案一:检出项目A源码进行构建。能获得项目A最新构件,但需要额外的构建操作,而且构建出问题也不好解决,低效
方案二:重复部署项目A,版本号不变。需要清理本地仓库相同版本号的项目A构件,不然无法下载A最新构件
方案三:不停更新版本。需要频繁修改POM文件,同时造成版本号的滥用。
方案四:将项目A的版本设置为快照版本。
Maven在发布项目快照版本时,会自动打上时间戳。如2.1-20091214.221414-13 表示 2009年12月14日22时14分14秒 第13次快照。通过时间戳,Maven就能找到该构件的最新快照版本。
默认情况下,Maven会每天检查一次更新(由仓库配置的updatePolicy控制),也可通过-U命令强制更新,如mvn clean install -U
从仓库解析依赖的机制
当本地仓库没有依赖构件时,Maven会自动从远程仓库下载;当依赖版本为快照版本时,Maven会自动找到最新的快照。其依赖解析机制如下
1)依赖范围是system,Maven直接从本地文件系统解析构件。
2)根据依赖坐标计算仓库路径,Maven直接从本地仓库寻找构件,如果发现相应构件,则解析成功。
3)本地仓库不存在,且依赖版本是显式的发布版本构件(1.0,1.2-beta-1),遍历所有远程仓库,发现后下载并解析使用
4)如果依赖版本是RELEASE和LATEST,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/maven-metadata.xml,将其与本地仓库的对应元数据合并后,计算出RELEASE或LATEST的真实的值,然后基于这个值检查本地和远程仓库。
5)如果依赖版本是SNAPSHOT,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/version/maven-metadata.xml,将其与本地仓库的对应元数据合并后,得到最新快照版本的值,然后基于该值检查本地仓库和远程仓库。
6)如果最后解析得到的构件版本是时间戳格式的快照,如1.4.1-20091104.121450-121,则复制其时间戳格式文件至非时间戳格式,如SNAPSHOT,并使用该非时间戳格式的构件。
Maven基于更新策略来检查更新,与远程仓库配置中<release>和<snapshot>中的子元素<enable>、<updatePolicy>有关,只有enable为true,才能访问该仓库对应发布版本/快照版本的构件信息,并基于updatePolicy更新策略检查更新,使用-U可以强制更新。
当Maven检查完更新策略,并决定检查依赖版本,就需要检查仓库元数据maven-metadata.xml
基于groupId和artifactId的maven-metadata.xml
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus</artifactId>
<versioning>
<latest>1.4.2-SNAPSHOT</latest> <!-- 指向最新版本 -->
<release>1.4.0</release> <!-- 指向最新发布版本 -->
<versions> <!-- 版本历史记录 -->
<version>1.3.5</version>
<version>1.3.6</version>
<version>1.4.0-SNAPSHOT</version>
<version>1.4.0.1-SNAPSHOT</version>
<version>1.4.1-SNAPSHOT</version>
<version>1.4.2-SNAPSHOT</version>
</versions>
<lastUpdated>20091214221557</lastUpdated> <!-- 记录最近更新时间 -->
</versioning>
</metadata>
该XML文件列出仓库中存在的该构件的所有可用的版本,Maven通过合并多个远程仓库及本地仓库的元数据,就能计算出基于所有仓库的latest和release分别是什么,然后再解析具体的构件。
Ps:不推荐依赖声明中使用LATEST和RELEASE,Maven随机解析到不同的构件,当构件发生变化,可能会造成项目构建失败,且不易排查。
当依赖版本是快照版本时,Maven也需要检查更新。
基于groupId、artifactId和version的maven-metadata.xml
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus</artifactId>
<version>1.4.2-SNAPSHOT</version>
<versioning>
<snapshot>
<timestamp>20091214.221414</timestamp>
<buildNumber>13</buildNumber>
</snapshot>
<lastUpdated>20091214221414</lastUpdated>
</versioning>
</metadata>
该XML文件的snapshot元素包含timestamp和buildNumber两个元素分别代表这一快照的时间戳和构建号。并由此可以得到该仓库中此快照的最新构件版本实际为1.4.2-20091214.221414-13,通过合并所有远程仓库和本地仓库的元数据,就能知道所有仓库中该构件的最新快照。
最后,元数据并非永远正确,当某些构件无法解析,或解析错误时,需手工或使用工具(如Nexus)对齐进行修复。
镜像
如果仓库X可以提供仓库Y存储的所有内容,那么就可以认定X是Y的一个镜像。
举个例子,http://maven.net.cn/content/g...://repol.maven.org/maven2在中国的镜像,由于地理位置因素,使用该镜像往往能够提供比中央仓库更快的服务。
编辑settings.xml,配置镜像
<settings>
...
<mirrors>
<mirror>
<id>...</id> <!-- 镜像仓库id -->
<name>...</name> <!-- 镜像仓库名称 -->
<url>...</url> <!-- 镜像仓库地址 -->
<mirrorOf>...</mirrorOf> <!-- 被镜像仓库表达式: *(匹配所有远程仓库) -->
</mirror>
</mirrors>
...
</settings>
mirrorOf元素用来匹配被镜像仓库(有点类似于Servlet的url-pattern表达式),当你要访问的远程仓库id满足mirrorOf表示式时,就会被拦截访问镜像服务器。表达式语法如下:
* | 匹配所有远程仓库 |
external:* | 匹配所有远程仓库,localhost除外 |
a,b | 匹配a和b仓库,多个仓库用,隔开 |
*,!a | 匹配所有远程仓库,a除外,使用感叹号将仓库从匹配中排除 |
仓库搜索服务
使用Maven进行日常开发时,获取依赖需要确切的依赖坐标,通过仓库搜索服务可以根据关键字得到Maven坐标。
Sonatype Nexus | http://repository.sonatype.org | 提供关键字搜索、类名搜索、坐标搜索、校验和搜索等功能,坐标和构件下载 |
aliyun Nexus | http://maven.aliyun.com | 同上,aliyun架设的公共Nexus仓库实例,服务较快 |
Jarvana | http://www.jarvana.com/jarvana | 提供基于关键字、类型搜索,坐标,构件下载。还支持浏览构件内部内容和便捷的Java文档浏览功能 |
MVNbrowser | http://www.mvnbrowser.com | 只提供关键字搜索,坐标,还可查看构件依赖于那些构件(Dependencies)以及该构件被哪些其他构件依赖(Referenced By) |
MVNrepository | http://mvnrepository.com | 提供关键字搜索,坐标,构件下载,依赖于被依赖关系信息,构件所含信息,还能提供一个简单图表,显示某个构件各版本间的大小变化 |