Maven 仓库
坐标和依赖是任何一个构件在 Maven 世界中的逻辑表达方式,而构件的物理表示方式是文
件,Maven 通过仓库来统一管理这些文件。
1. Maven 仓库是什么
任何一个依赖、插件或者项目构建的输出,都可以称为构件。
任何一个构件都有一组坐标唯一标识,例如:
<groupId>com.lichee</groupId> <artifactId>lichee-core</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging>
a) 位置
得益于坐标机制,任何 Maven 项目使用任何一个构件的方式都是完全相同的,
Maven 可以在某个位置统一存储所有 Maven 项目共享的构件,这个统一的位置就是仓库。
b) 寻找
实际的 Maven 项目将不再各自存储其依赖文件,只需要声明坐标,
Maven 会自动根据坐标找到仓库中的构件。
c) 重用
为了实现重用,项目构建完毕后生成的构件也可以安装或者部署到仓库,
供其他项目的使用。
2. 仓库布局
任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,
这便是 Maven 的仓库布局方式。
a) 路径对应关系
groupId/artifactId/version/artifactId-version.packaging。
例如:log4j:logj4j:1.2.16依赖,对应仓库的路径:log4j/log4j/1.2.16/log4j-1.2.16.jar。
b) 存储方式
Maven仓库是基于简单文件系统存储的,了解了存储方式,当遇到一个些仓库相关的问题,
可以很方便的查找相关文件,方便定位问题。
3. 仓库分类
Maven 根据坐标寻找构件的时候,会首先查看本地仓库,有则直接使用,
本地仓库没有或者查看是否有更新的构件版本,Maven 就会去远程仓库查找,
发现构件后下载到本地仓库使用。如果本地仓库和远程仓库都没有需要的构件,
Maven 就会报错。
a) 本地仓库
Maven 的项目目录下,没有 lib 这样用来存放依赖文件的目录。当需要使用依赖文件的时候,
基于坐标使用本地仓库的依赖文件。
i. 仓库位置
不管什么操作系统,每个用户目录的下都有一个.m2/repository/的仓库目录
(Linux、或者 mac os,以“.”开头的文件或者目录默认都是隐藏的)。
ii. 自定义本地仓库目录地址
由于某些原因(例如 C 盘空间不够),编辑~/.m2/settings.xml。
<settings>
<localRepository>D:\java\repository</localRepository>
</settings>
1) ~/.m2/settings.xml
此文件默认情况下不存在,需要用户从 Maven 安装目录手动拷贝
($ M2_HOME/conf/settings.xml),再进行编辑。
iii. 安装本地项目构件(mvn install)
Install 插件的 install 目标将项目的构件输出文件安装到本地仓库。
iv. 时期
本地仓库的目录开始是不存在的,必须要执行一条 maven 的命令,
maven才会开始创建本地仓库。
b) 远程仓库
由于最开始本地仓库也是空的,Maven 必须要知道至少一个可用的远程仓库,
才能执行 Maven 下载这些构件。
i. 中央仓库(默认)
Maven 核心自带的远程仓库,包含了绝大部分开源构件,默认配置下,
当本地仓库没有 Maven 需要的构件时,它就会尝试从中央仓库下载。
ii. 私服
另外一种特俗的远程仓库,为了节省带宽和时间,应该在局域网内架设一个私有的仓库服务器,
用其代理所有外部的远程仓库,供局域网内部的 Maven 用户使用。
1) 节省自己的外网带宽
2) 加快 Maven 构建
3) 部署第三方构件
4) 提高稳定性、增强控制
5) 降低中央仓库的负荷
iii. 其他公共库
除了中央仓库和私服,还有很多其他公开的远程仓库。例如:Java net Maven库,
和 JBoss Maven 库。
iv. 远程仓库的配置
<repositories> <repository> <id>nexus</id> <name>Nexus</name> <url>http://127.0.0.1/nexus/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> <layout>default</layout> </repository> </repositories>
1) repositories
在此元素下可以使用 repository 子元素声明一个或者多个远程仓库。
2) id
id 必须唯一,不可以与自带的中央仓库 central 重名,否则中央仓库的配置会被覆盖。
3) url
指向仓库的地址,都基于 http 协议。
4) releases
发布版本构件。
5) snapshots
快照版本构件。
6) enabled
true 开启、fase 关闭。
7) layout
值 default 表示仓库的布局是 Maven2 及 Maven3 的默认布局,而不是Maven1 的布局。
v. Releases 和 snapshots 补充
除了 enabled,还有其他元素。
<snapshots> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> <checksumPolicy>ignore</checksumPolicy> </snapshots>
1) updatePolicy
Maven 从远程仓库检查更新的频率。
daily -每天检查一次。
never - 从不检查更新。
always - 每次构建都检查更新。
interval:X - 每隔 X 分钟检查一次更新(X 为任意整数)。
2) checksumPolicy
Maven 检查检验和文件的策略。当构件被部署到 Maven 仓库中时,
会同时部署对应的校验和文件。当下载构件的时候,Maven 会验证校验和文件,
校验失败的时候:
warn - 默认值,执行构建时输出警告信息。
fail - 遇到校验和错误就让构建失败。
ignore - 完全忽略校验和错误。
vi. 认证
为了防止非法的仓库访问,管理员为每个仓库提供了一组用户名及密码。此时,
为了能访问仓库的内容,就需要配置认证信息。
<servers> <server> <id>releases</id> <username>admin</username> <password>123456</password> </server> <servers>
1) 不同于配置仓库信息
仓库信息可以直接配置在 pom.xml 文件中,但是认证必须配置在
settings.xml 文件中,因为 settings 文件一般在本机,而 pom.xml 在
代码中供所有成员访问,settings.xml 配置认证信息更安全。
2) servers
servers 可以配置多个 server 子元素。
3) id
id 必须和 pom 中需要认证的 repository 元素的 id 完全一致。
4) repo-user
认证用户名。
5) repo-pwd
认证密码。
vii. 部署至远程仓库
<distributionManagement> <repository> <id>pro-release</id> <name>Proj Release Repository</name> <url>http://localhost:8081/nexus/content/repositories/ RestBus-Releases</url> </repository> <snapshotRepository> <id>pro-snapshot</id> <name>Proj Snapshot Repository</name> <url>http://localhost:8081/nexus/content/repositories/ RestBus-Snapshots</url> </snapshotRepository> </distributionManagement>
1) repository
发布版本构件仓库。
2) snapshotRepository
快照版本构件仓库。
3) 注意点
这两个元素下都需要配置 id、name 和 url。
id:为该远程仓库的唯一标识。
name:只是方便阅读。
url:该仓库的地址。
4) 部署需要认证
5) 部署指令
mvn deploy
4. 快照版本
Maven 世界中,任何一个项目或者构件都必须有自己的版本。
a) Maven 为什么要区分快照版本和发布版本?
正在开发的 2 个模块,从私服下载彼此进行构建,一旦一方提交更新,
另一方本地依赖如何简化处理。
i. 方案一“一方迁出对方的源码进行构建”
不得不多出版本控制和 Maven 操作不说,当构建失败的时候,更是一头雾水,
还不得不找对方来解决构建问题。
ii. 方案二“重复部署某一版本”
虽然一方可以保证私服仓库中的构件是最新的,但同样的版本和同样的坐标就意味着同样的构件。 因此另一方本地仓库一旦包含了此构件,Maven 就不会对照远程仓库进行更新,
除非执行命令之前,手工删除本地仓库中的此构件,手工干预的做法显然是不可取的。
iii. 方案三“不停更新版本”
首先双方都需要频繁的更改 POM,这只是 2 个模块,模块越多,修改工作就越复杂,
涉及更多的 POM 修改。其次,大量的版本其实仅仅包含了细小的差异,
有时候是对版本号的滥用。
iv. Maven 的快照版本机制
只需要讲版本的编号设定为 xx-SNAPSHOT,发布到私服中,在发布过程中,
Maven 会自动为构件打上时间戳。例如:xx-20140303.161144-13,
代表 2014-3-3 16:11:44 的第十三次快照版本。有了时间戳,
Maven 就能随时找到远程仓库中该构件的最新版本。当另一方进行构件的时候,
Maven 会自动检查最新的构件,并下载到本地仓库。默认情况是每天检查一次更新,
但也可以使用 updatePolicy 控制(3.b.v.1 章节),也可以使用命令行-U 参数,
强制让 Maven 检查更新,例如:mvn install-U。
v. 有大量带有不同时间戳的构件,决定它的不稳定性
b) 使用经验
i. 项目不应该依赖于任何组织外部的快照版本依赖。
ii. 快照版本的不稳定性可能造成潜在的危险。
5. 仓库解析依赖的机制
当本地仓库没有依赖构件的时候,Maven 会主动从远程库下载,当依赖版本为快照版本的时候,
Maven 会自动找到最新的快照。
a) 当依赖范围是 system 时,Maven 直接从本地文件系统解析构件。
b) 根据依赖坐标计算仓库路径,在本地仓库寻找,发现后则解析成功。
c) 本地仓库不存在,遍历所有的远程仓库,发现后下载、并解析使用
d) 如果依赖的版本是 RELEAES 或者 LATEST
基于更新策略读取所有远程仓库中的元素据 groupId/artifactId/maven-metadata.xml,
将其与本地仓库的元素据合并,计算出 RELEASE 或者 LATEST 真实的值,
基于这个值来检查本地和远程仓库。
e) 如果依赖的版本是 SNAPSHOT
则基于更新策略读取所有远程仓库的元素据,groupId/artifactId/version//maven-metadata.xml,
将其与本地仓库的元素据合并,得到最新快照版本的值,然后基于这个值来检查本地仓库,
或者从远程仓库下载。
f) 如果最后解析得到的构件是时间戳格式的快照
如:xx-20140303.161144-13,则复制其时间戳格式的文件至非时间戳格式,
如 SNAPSHOT,并使用该非时间戳格式的构件。
g) 当版本不明晰的时候,如 RELEASE、LATEST、SNAPSHOT 时
Maven 就需要基于更新远程仓库的更新策略来检查更新,这种不明晰版本的做法,
是非常不推荐的做法。Maven3 也不再支持在插件配置中使用这三种方式,
如果不配置插件版本,其效果和 RELEASE 一样,只解析最新发布版构件。
h) 仓库元素据并不是永远正确的,需要手工或者使用工具(Nexus)进行修复。
6. 镜像
如果仓库 X 可以提供仓库 Y 存储的所有内容,那么就可以认为 X 是 Y 的一个镜像。
任何一个可以从仓库 Y 获得的构件,都能够从它的镜像中获取。
a) 镜像存在原因
由于地理位置的因素,该镜像往往能够提供比中央仓库更快的服务(网速)。
可以配置 Maven 使用镜像来替代中央仓库。
b) 配置镜像
<mirror> <id>nexus</id> <name>nexus</name> <url>http://127.0.0.1/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror>
i. mirrorOf
值为 central,表示该配置为中央仓库的镜像,任何对于中央仓库的请求都会转至该镜像,
也可以用同样的方法去配置其他仓库的镜像。
ii. id、name、url
和一般仓库的配置无差异,标识符、名称以及地址。
c) 最佳实践
i. 结合私服
镜像的一个更为常见的用法是结合私服。因为私服可以带来任何外部的公共仓库,
对于组织内部来说,使用一个私服地址就等于使用了所有需要的外部仓库。
可以将配置集中到私服,从而简化 Maven 本身的配置。私服就是所有仓库的镜像。
ii. <mirrorOf>*</mirrorOf>
当值为*的时候,表示该配置是所有 Maven 仓库的镜像,任何对于远程仓库的请求,
都会被转至配置的 url 私服。
iii. <mirrorOf>external:*</mirrorOf>
匹配所有远程仓库,使用 localhost 的和使用 file://协议的除外。也就是说,
匹配所有不在本机上的远程仓库。
iv. <mirrorOf>repo1,repo2</mirrorOf>
匹配仓库 repo1 和 repo2,使用逗号分隔多个远程仓库。
v. <mirrorOf>*,! repo1</mirrorOf>
匹配所有的远程仓库,除 repo1 外,使用感叹号将仓库从匹配中排除。
d) 注意
当镜像仓库不稳定或者停止服务的时候,Maven 将无法下载构件。
7. 仓库搜索服务
a) Sonatype Nexus
b) Jarvana
c) MVNbrowser
d) MVNrepository