maven中的构件构件成功后,以文件的方式保存在磁盘中,maven通过仓库来管理这些文件。
何为maven仓库
在maven中,任何一个依赖、插件、或者项目的构建的输出,都可以称之为构件。构件生成后,需要存储到一个指定的位置,我们称之为仓库,仓库根据指定的规则对maven构件进行管理。仓库中存储了大量的构件,搭建项目时,可以直接从仓库中获取所需要的构件。
仓库的布局
每个构件都有唯一的坐标,maven仓库根据坐标和对应的规则来生成构建唯一的存储路径。对应规则如下:
groupId/artifactId/version/artifactId-version[-classifier].packaging
maven仓库布局源码如下:
private static final char PATH_SEPARATOR = "/";
private static final char GROUP_SEPARATOR = ".";
private static final char ARTIFACT_SEPARATOR = "-";
public String pathOf(Artifact artifact)
{
ArtifactHandler artifactHandler = artifact.getArtifactHandler();
StringBuffer path = new StringBuffer(128);
path.append(formatAsDirectory(artifact.getGroupId())).append(PATH_SEPERATOR);
path.append(artifact.getArtifactId()).append(PATH_SEPERATOR);
path.append(artifact.getBaseVersion()).append(PATH_SEPERATOR);
path.append(artifact.getArtifactId()).append(ARTIFACT_SEPERATOR).append(artifact.getVersion());
if(artifact.hasClassifier())
{
path.append(ARTIFACT_SEPERATOR).append(artifact.getClassifier());
}
if(artifactHandler.getExtension() != null && artifactHandler.getExtension().length > 0)
{
path.append(GROUP_SEPARATOR).append(artifactHandler.getExtension());
}
return path.toString();
}
private String formatAsDirectory(String directory)
{
return directory.replace(GROUP_SEPARATOR,PATH_SEPARATOR);
}
下面以一个具体的例子来实例:
groupId : org.testing
artifactId : testing
version : 5.8
classifier : jdk15
packaging : jar
1、基于groupId准备路径,formatAsDirectory()将groupId中的#.#转换为#/#,该实例中将 org.testing转换为 org/testing/。
2、基于artifactId准备路径,在前面的基础之上加上artifactId及一个路径分隔符,该实例中为org/testing/testing/。
3、基于版本信息,在前面的基础之上加上version和路径分隔符,该实例中为org/testing/testing/5.8/。
4、基于构件名称,在前面的基础之上加上artifactId、构件分隔符、版本号,该实例中未org/testing/testing/5.8/artifact-5.8,这里使用的artifactId.getVersion(),上一步使用的是artifactId.getBaseVersion(),baseVersion只包含数字。
5、基于classifier,如果构件有classifier,就加上构件分隔符和classifier,该例中为org/testing/testing/5.8/artifact-5.8-jdk5。
6、基于构件的extension,若extension存在,则加上句点分隔符和extension,extension是从artifactHandler获取,artifactHandler是由项目的packagin决定的,可以说packagin决定了构件的扩展名,该例的packaging是jar,因此最终的路径为:rg/testing/testing/5.8/artifact-5.8-jdk5.jar。
maven系统是基于简单文件系统存储的,当遇到一些与仓库相关的问题时,可以很方便的查找相关文件,定位问题。
仓库的分类
对于maven来说,仓库只分为两类:本地仓库和远程仓库。maven根据坐标查找构件,首先会查看本地仓库,如果本地仓库没有,就回去远程仓库查找,如果都没有,maven就会报错。
远程仓库包括中央仓库、私人仓库、其它远程仓库。
中央仓库是maven核心自带的仓库,它包含了绝大部分构件,在默认配置下,当本地库没有构件时,maven从中央仓库下载构件。
私人仓库是一种特殊菜单远程仓库,为了节省带宽和时间,应该在局域网内架设一个私人仓库,用其代理所有的远程仓库,同时内部的项目也可以部署到私人仓库中。
其它远程仓库,除了中央仓库和私人仓库外,还有很多其它公开的远程仓库,比如java.net.maven库(http://download.java.net/maven/2)和jboss maven库(http://repository.jboss.com/maven2)等。
1、本地仓库
maven本地仓库存储目录为.m2/repository/,可以通过设置.m2/settings.xml(默认情况下本地仓库下不存在settings.xml,需要复制maven安装目录$ M2_HOME/conf/settings文件),设置localRepository元素的值为想要的仓库地址,设置如下:
<settings>
<localRepository>d:\java\maven\repository</localRepository>
</settings>
一个构件只有在本地仓库中存在后,才能被其它maven项目使用,可以使用mvn clean install命令把项目导入到本地仓库中。
2、远程仓库
maven安装成功后,本地仓库是不存在的,只有执行mvn指令后,maven才会生成本地仓库。本地仓库类似书房的概率,远程仓库类似书店的概念,对于一个用户来说,书房只有一个,书店可以存在多个,用户从书店买书后,存放到书房中。
3、中央仓库
由于本地仓库是空的,所以至少需要配置一个默认的远程仓库,才能在执行命令的时候从远程仓库或者构件,这个默认的远程仓库就是中央仓库。maven的安装文件中自带了中央仓库的配置。打开$M2_HOME/lib/maven-model-builder-3.0.jar,在org/apache/maven/model/pom-4.0.0.xml文件配置中,可以看到如下配置:
<project>
<repositories>
<repository>
<id>central>
<name>maven reporitory test</name>
<url>http://repo1.maven.rog/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
包含这段配置的文件是所有Maven项目都会继承的超级POM文件。
4、私服
私服是一种特殊的远程仓库,它是搭建在局域网内的仓库服务。私服代理广域网上的远程仓库,供局域网内的用户使用。当maven需要下载构件时,先从私服获取,如果私服不存在,则从远程仓库获取,缓存到私服后,在为本地用户提供服务。同时一些内部项目也可以放置到私服上,为大家提供服务。
graph LR
A[maven用户] --> |下载构件| B[私服]
B[私服] --> |缓存构件| c[远程仓库]
私服的好处:
1、节省外网带宽。
2、加速maven构件。
3、部署第三方构件。
4、提高稳定性、加强控制。
5、降低中央仓库的负荷。
远程仓库的设置
1、远程仓库的配置
远程仓库的设置如下:
<project>
<repositories>
<repository>
<id>jboss</id>
<name maven jboss repository</name>
<url>http://repository.jboss.com/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<layout>default</layout>
</repository>
</repositories>
</project>
在repositories元素下,可以声明多个repository,maven自带的中央仓库名称为central,如果其它仓库使用该名字,将会覆盖中央仓库的配置。
layout为default表示仓库基于默认的布局。
releases和snapshots元素非常重要,它们用来控制发布版本和快照版本的下载。enables为true表示支持下载,enables为false表示关闭下载。除了enables子元素外,他们还包含updatePolicy和checksumPolicy两个子元素。
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
updatePolicy配置maven从远程仓库检查更新的频率,daily表示每天更新一次,never表示从不更新,always表示每次构建都检查,interval:x表示每隔X分钟就检查一次更新。
checksumPolicy配置表示maven检查文件和配置的策略,当构件被部署到仓库时,同时部署对应的检验和文件,在下载构件时,同时验证检验和文件,如果检验和验证失败,maven要怎么处理,warm表示对检验和验证失败进行警告,fail表示遇到检验和验证失败就组件构件失败,ignore表示忽略检验和验证失败。
2、远程仓库的认证
大部分远程仓库可以直接访问,但是出于安全的考虑,我们需要提供认证信息才能访问一些仓库。仓库认证信息配置在settings.xml中,因为文件需要提交到远程仓库,所以不能配置到pom.xml中,认证信息配置如下:
<settings>
<servers>
<server>
<id>my-repository</id>
<username>repo-user</username>
<password>repo-pass</password>
</server>
</servers>
</settings>
settings.xml中的server元素的id必须和pom.xml中repository元素的id一致,正是这个id将认证信息和配置信息联系在一起。
3、部署至远程仓库
maven除了能对项目进行编译、测试、打包外,还能降生成的构件部署到仓库中,首先需要在pom.xml中配置远程仓库的配置,具体如下:
<distributionManagement>
<repository>
<id></id>
<name></name>
<url></url>
</repository>
<snapshotRepository>
<id></id>
<name></name>
<url></url>
</snapshotRepository>
</distributionManagement>
distributionManagement包含repository和snapshotRepository两个子元素,前者表示发布版本的仓库,后者表示快照版本的仓库。往远程仓库发布应用时,往往需要认证,认证方式即在settings中配置server节点,和之前一样。配置正确后,运行mvn clean deploy命令,发布版本到对应的仓库。
快照版本
在maven中任何一个项目都有自己的版本,版本值可能是1.0.0,1.3-alpha-4,2.1-SNAPSHOT或者2.1-20091111.194822。其中1.0.0和1.3-alpha-4是稳定的发布版本,2.1-SNAPSHOT和2.1-20091111.194822是不稳定的快照版本。
快照版本的意义在于,设置为快照版本后,不需要频繁的更改版本号,maven自动在快照版本的每次提交后,添加时间戳,即年月日时分秒,版本号不需要变动,版本随时在变动,而且这种变动对开发方和使用方都是透明的。开发方设置为快照版本后,每次提交,面上的快照版本号都是一样的,maven会自动添加上时间戳来区分每次提交。使用方配置了快照版本后,maven自动根据时间戳获取最新的版本。因为快照版本的不稳定性,快照版本只应该在项目内部开发过程中使用,等到功能稳定后,需要发布稳定版本。
从仓库解析依赖的机制
maven是根据怎么的规则解析依赖,并从仓库下载构件的呢,具体如下:
1、当依赖范围为system,maven直接从本地文件系统获取解析构件。
2、根据依赖坐标计算仓库路径后,尝试从本地仓库寻找构件,如果发现相应构件,解析成功。
3、在本地仓库不存在相应的构件后,如果依赖的版本是显示的发布版本构件,如1.2、2.1-beta-1等,则遍历所有的远程仓库后,下载并解析使用。
4、如果依赖的版本是RELEASE或者LATEST,则基于更新策略读取所有远程仓库的元数据文件groupId/artifactId/maven-metadata.xml,将其与本地仓库的对应元数据合并后,得到最新的快照版本的值,然后基于该值检查本地仓库或从远程仓库下载。
5、如果依赖的版本是SNAPSHOT,则基于更新策略读取所有远程仓库的元数据文件groupId/artifactId/version/maven-metadata.xml,将其与本地的元数据合并后,然后基于该值检查本地仓库或从远程仓库下载。
6、如果最后解析得到的构件版本是时间戳格式的快照,则复制其时间戳格式的文件为非时间戳格式,如SNAPSHOT,并使用该非时间戳格式的文件。
当依赖的版本不明晰的时候,如release、latest和snapshot,maven就需要基于更新远程仓库的策略来检查更新,有一些配置与此有关,首先是 和 ,只有仓库开启了对于发布版本的支持时,才能访问该版本相关的构件,其次要注意元素,该元素指定了版本更新的频率,包括每日检查更新、永远检查更新、从不检查更新、自定义时间间隔更新。最后,用户还可以在命令行中加入-U参数,强制检查更新。
releas和latest版本分别对应了仓库中存在该构件的最新发布版本和最新版本(包含SNAPSHOT),而这两个最新是基于groupId/artifactId/maven-metadata.xml计算出来的,配置如下:
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compile-plugin</artifactId>
<versioning>
<latest>1.4.2-SNAPSHOT</latest>
<release>1.4.0</release>
<verions>
<version>1.3.5</version>
<version>1.4.0</version>
<version>1.4.1-SNAPSHOT</version>
<version>1.4.2-SNAPSHOT</version>
</versions>
<lastUpdated>20181107210504</lastUpdated>
</versioning>
</metatada>
xml中列出了仓库中存在的该构件的所有的版本,latest指向了最新的那个版本,即1.4.2-SNAPSHOT,release指向了最新的发布版本,即1.4.0。在依赖声明中,不建议使用release和latest配置,因为maven随时都可能解析到不同的版本,导致版本不稳定。
当依赖版本设置为快照版本时,maven也需要检查更新,此时maven会检查仓库元数据groupId/artifactId/version/maven-metadata.xml文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-help-plugin</artifactId>
<version>1.1.1-SNAPSHOT</version>
<versioning>
<snapshot>
<timestamp>20181108.063222</timestamp>
<buildNumber>15</buildNumber>
</snapshot>
<lastUpdated>20181108063222</lastUpdated>
</versioning>
</metadata>
xml中snapshot元素包含timestamp和buildNumber两个子元素,代表构件时间和构件次数,基于这两个元素可以得到该仓库中此构件的最新构件版本实际为1.1.1-20181108.063222-15,通过合并本地仓库和远程仓库的元数据,maven可以获取所有仓库中该构件的最新版本。
镜像
如果仓库X可以提供仓库Y的所有内容,那么X是Y的镜像。镜像往往可以提供比中央仓库更快的服务,因此可以配置镜像来替代中央仓库,编辑settings.xml,配置代码如下:
<settings>
<mirrors>
<mirror>
<id>maven.net.cn</id>
<name>central repository mirror</name>
<url>http://maven.net.cn/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
<settings>
mirrorOf为central表明配置的是中央仓库的镜像,任何对于中央仓库的请求会转至该镜像。关于镜像的例外一个用法是结合私服,由于私服可以代理任何外部的公共仓库,对于内部用户来说,使用了私服就等于使用了所有的外部仓库。配置如下:
<settings>
<mirros>
<mirro>
<id></id>
<name></name>
<url></url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirros>
</settings>
匹配所有远程仓库。
external: 匹配所有原创仓库,本地的除外,即匹配所有不在本机上的远程仓库。
repo1,repo2 匹配仓库repo1,repo2,多个仓库之间以,分隔。
*,!repo1 匹配所有远程仓库,repo1除外,使用感叹号来排除仓库。
仓库搜索服务
在使用maven开发过程中,如果寻找依赖,也是个问题,下面介绍几种常见的公共maven仓库搜索服务。
Sonatype Nexus
http://repositroy.sonatype.rog/ 打开比较慢。
jarvana
http://www.jarvana.com/jarvana/ 网址打不开
MVNbrowser
MVNrepository
http://mvnrepository.com 界面比较清新
上面介绍的四种搜索服务都代理了主流的maven仓库,如center,jboss,java.net等。
小结
本章深入阐述了maven仓库的概念,包括仓库的由来、仓库的布局、仓库的类型、远程仓库、仓库的解析依赖机制、镜像、搜索服务等功能。