三、仓库与镜像

目前在看nexus私服章节的知识时需要用到仓库与镜像的知识,正好通过简书把仓库和镜像章节的笔记整理一下

TIM图片20170722182844.gif

仓库

maven可以在某个位置统一存储所有maven项目共享的构件,这个统一的位置就称之为仓库
任何一个构件都有一个唯一的坐标,通过这个唯一的坐标就可以定义当前构件在仓库中的唯一路径,该路径与maven的坐标大致关系为: groupId/artifactId/version/artifact-version.packaging

jar包路径的生成
maven中任何构件的路径都不是凭空得来的, 而是通过坐标元素计算的,并且可以确定的是每一个构件的路径都是唯一的(假定以构件坐标groupId:artifactId:version:classifier:packaging=org.testng:tesning:5.8:jdk15:jar为例):

  1. 基于构建的groupId准备路径,将groupId中的.分隔符转变为文件分隔符/,如org/testnng
  2. 基于构建的artifactId准备路径,在前面的路径基础上加上文件分隔符/再拼接上artifactId,如org/testng/testng
  3. 使用版本信息,在前面路径基础上加上文件分隔符和版本,如org/testng/testng/5.8
  4. 依次加上文件分隔符、artifactId、构建分隔符-、版本信息,如org/testng/testng/5.8/testng-5.8
  5. 如果构件存在classifier,就加上构建分隔符-classifier,如org/testng/testng/5.8/testng-5.8-jdk15
  6. 检查构件的extension,若extension存在,则加上句点分隔符.extension(这里的extension是由packaging决定的),如org/testng/testng/5.8/testng-5.8-jdk15.jar
    在了解了构件路径之后,当我们遇到maven无法找到项目声明的依赖时,可以查看对应构件在仓库中的位置中是否存在,如果不存在则检查是否有其他版本可用,具体可以参考mvnrepository仓库中的依赖说明

仓库分类
从大的范围来说,maven其实只有两种类型的仓库:本地仓库和远程仓库,在远程仓库中又分为三大类:中央仓库、私服、第三方仓库,他们之间的关系如下:

test.png

本地仓库
maven项目中是没有/lib这种用于存放依赖文件的目录的,当maven需要编译、测试时,它总是基于坐标使用本地仓库中的依赖文件
本地仓库默认在【用户目录/.m2/repository】下,windows系统环境下目录在:C:\Users\User\.m2\repository下,而linux系统环境则是在/home/User/.m2/repository,需要注意的是linux中以.开头的文件都是隐藏文件,可以通过ls -al命令来查看所有文件
修改maven配置属性的几点建议
在某些情况下,我们可能会更新maven配置文件里面的属性,在不同场景中发挥特定的效果
这时我们可以有两种方式来更新maven配置文件:

  • 直接修改maven安装根目录/conf/settings.xml配置文件
  • 将安装根目录/conf/settings.xml拷贝一份到.m2/settings.xml中,通过修改.m2/settings.xml中的配置来达到项目要求的效果(推荐这么做)

原因有二:

  • 全局配置会影响到其他用户
  • maven升级时会覆盖全局配置文件,导致用户的配置失效需要重新配置,而用户目录下的settings.xml则不受限制

不同的项目需要相互引用怎么办呢?
别着急,且听我娓娓道来,maven已经考虑到这一切,我们只需要通过一条简单命令将被引用的项目打包到本地仓库,这样当前项目才会被其他项目发现并引用,打包项目到仓库命令可以通过执行 mvn clean install轻松搞定,这样其他项目根据配置的项目坐标到本地仓库去查找指定项目依赖时就会发现当前依赖的项目已经被安装,(maven执行这条命令时涉及到了又一大核心maven生命周期,这在后续会专门介绍,这里的重点是仓库,这里就不细讲了)

远程仓库
远程仓库是maven下载依赖构件的源,当maven安装之后,在没有执行maven命令之前本地仓库目录是没有被创建的,当执行maven的第一条命令之后,本地仓库会被建立,同时访问远程仓库下载依赖的构件
maven默认的本地仓库路径可以在配置文件settings.xml中自定义,具体配置如下:
<localRepository>G:\workspace\repository</localRepository>
可以通俗的打个比方,本地仓库就好比您的书房,而远程仓库就好比在线或者实体书店,你需要看书时先会在书房中找,如果没有就去网上或者实体书店买并纳入自己的书房,之后就可以从书房中获取到这本书了
下面提供几个实用的仓库,如果配置了私服,可以在私服上代理这些常用仓库

  1. aliyun仓库
    http://maven.aliyun.com/nexus/content/groups/public/
  2. forge仓库
    https://repository.sonatype.org/content/groups/forge/

中央仓库
中央仓库是maven默认的一个可被访问的远程仓库,配置的具体位置可以查看${M2_HOME}/lib/maven-model-builder-3.3.9.jar中的org/apache/maven/model/pom-4.0.0.xml(超级pom)
下面是截取的超级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>

私服
私服是一种特殊的远程仓库,它是架设在局域网内部的一种仓库服务,代理广域网上的远程仓库
用户请求下载某一jar包时,先从私服中找,如果私服中不存在,则遍历远程仓库并下载指定的构件到私服中,并提供给本地仓库下载,最后供用户项目使用
使用私服的好处:

  1. 减少网络带宽,所有请求都访问私服,而私服则只会访问一次远程仓库,也就是只走一次外网环境
  2. 加速maven的构建速度,不停的请求远程仓库是十分耗时的操作,但maven的一些内部机制(如快照更新检查,它是什么鬼,后面做解释),所以maven在构建的时候会不停的检查远程仓库数据
  3. 部署第三方构建,私服允许将组织内部私有构件部署上去,只有组织内部的人员有权限访问此构件,对于外部用户是透明的
  4. 降低中央仓库的负荷,所有对中央仓库的请求都集中到了私服上,而当构件不存在时,私服也只是请求一次中央仓库下载对应的构件
  5. 提高稳定性,增强控制maven的构件高度依赖于仓库,如果没有网络时,maven构件会失败,但是如果在内网架设私服,那么对外网的访问则由私服代理了,确保maven在无网络环境下也可以正常工作

配置远程仓库
在项目中,除了由maven所提供的中央仓库之外,我们还可以配置其他更易于访问的远程仓库,需要修改项目根目录下的pom.xml文件

<repositories>
    <repository>
      <id>jboss</id>
      <name>JBoss Repository</name>
      <url>http://repository.jboss.com/maven2/ </url>
      <layout>default</layout>
      <release>
             <enabled>true</enabled>
      </release>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
     </repository>
  </repositories>

上面配置了一个jboss的远程仓库,一般情况下,仓库的url协议都是基于http协议的,所以您可以直接在浏览器中访问当前url,看是否可以访问

test2.png

需要注意的是,在配置远程仓库时,所有仓库的id是唯一的,尤其需要注意的是,maven中央仓库的idcentral,如果其他仓库使用了此id则中央仓库就被覆盖了,如此用户也就访问不到中央仓库了
下面对releases,snapshots下面的参数做一个介绍

<snapshot>
    <enabled></enabled> 
    <updatePolicy></updatePolicy>
    <checkSumPolicy></checkSumPolicy>
</snapshot>

enabled表示从该仓库下载快照版本的构件是否允许
updatePolicy有下面几种取值
daily 每天更新一次仓库数据 (默认) never: 从不更新仓库数据 period: 隔一定的周期更新一次仓库数据 always: 每一次构建都更新仓库数据
checkSumPolicy用于检查从远程仓库下载下来的jar是否正确,如果错误又要如何处理,下面给出了几种情况
ignore:完全忽略构件的校验 warn: 当构件校验和验证失败时给出提示(默认) fail: 当构件校验失败时就让构建失败
远程仓库的认证server
大部分的远程仓库都无需认证就可直接访问,有时候出于安全考虑,我们需要提供认证信息才能访问远程仓库,这时管理员可以为仓库设置用户名密码,此时访问该远程仓库就需要用户提供正确的认证信息,可以在settings.xml中针对某一个仓库配置认证信息
认证信息与仓库信息不同,需要配置在本机的maven配置文件settings.xml中,这是考虑到认证信息的安全性

<settings>
    <server>
        <id>repositoryid</id>
        <username>repo-user</username>
        <password>repo-pass</password>
    </server>
</settings>

这里需要注意的是server配置的id要与pom中配置的需要认证的repositoryid一致

部署至远程仓库
要想把项目提供给其他模块使用,我们需要把自己的项目模块部署到仓库中供其他模块下载引用,我们需要配置distributeManagement部署信息

<project>
    <distributeManagement>
        <repository>
            <id>remote-repository-id</id>
            <name>remote-repository-name</name>
            <url>proj-release</url>
        </repository>
        <snapshotRepository>
            <id>remote-repository-id</id>
            <name>remote-repository-name</name>
            <url>proj-snapshot</url>
        </snapshotRepository>
    </distributeManagement>
</project>

repository表示发布版本的仓库
snapshotRepository表示快照版本的仓库
当部署信息配置好之后就可以使用命令
mvn clean deploy
将项目打包安装到本地仓库并部署到远程仓库中

快照版本机制snapshot
快照版本要解决的问题是控制未发布版本的部署更新操作,假设A,B模块都处于未发布版本,现在B需要依赖A,如果不考虑快照版本的情况下,B可以进行下面几种方案来引用A

  1. B自己获取A项目源码进行构建,这可以确保B获取到A最新的代码,但B不得不去构建模块A,如果A在构建过程中出现问题,B可能是抓狂的
  2. A重复部署模块特定版本,比如B引用A的2.1版本,那么就基于A的2.1版本重复部署,虽然A能确保2.1版本代码是最新的,但是对于B来说确实未知的,因为相同的版本意味着同样的构件,除非B先清除本地仓库中的A2.1,这种需要B手工干预的方式也是不可取的
  3. AB不停的更新版本,这要求AB需要不停的更换版本,导致对版本的滥用

maven的快照版本机制就是为了解决上面的问题,需要将A的版本设置为2.1-SNAPSHOT,在发布构成中,maven会自动为A打上时间戳,当构件B时,maven会自动从仓库中检查模块A2.1-SNAPSHOT的最新版本,当然这项特性与仓库配置的updatePolicy有关,仓库配置如下:

<repository>
    <id></id>
    <name></name>
    <url></url>
    <releases>
        <enabled>true</enabled>
        <updatePolicy>daily</updatePolicy>
        <checksumPolicy>warn</checksumPolicy>
    </releases>
</repository>

仓库解析依赖的过程
当本地仓库没有依赖的时候,maven会自动从远程仓库下载,当依赖版本为快照版本的时候,maven会自动找到最新的快照。那么它是如何找的,具体的依赖机制如下:

  1. 当依赖范围(scope)为system的时候,maven会直接从指定的systemPath系统路径查找指定的依赖
  2. 根据坐标计算依赖在本地仓库中的位置,如果在指定的位置发现了构件则解析成功
  3. 在本地仓库不存在依赖的情况下,如果依赖的版本是显示的发布版本如1.2,1.2-beta-1,则遍历所有远程仓库,发现后下载到本地仓库
  4. 如果依赖的版本是RELEASE或者LASTEST,则基于更新策略读取所有远程仓库中的元数据/groupId/artifactId/metadata.xml,将其与本地仓库的元数据结合后计算出真实的RELEASE或者LATEST版本,再基于该版本查找仓库中的依赖,重复2,3
  5. 当发现依赖的版本是SNAPSHOT,则基于更新策略读取所有远程仓库中的元数据roupId/artifactId/version/metadata.xml,将其与本地仓库的元数据结合后计算出真实的快照版本并重复2,3
  6. 如果解析到的版本是时间戳格式的快照,如2.1-20091214.221414-13,则替换为2.1-SNAPSHOT这种非时间戳格式的形式

以上4.5.6,在maven3之后无效,maven3默认只允许解析最新的发布版本的构件
当然还与仓库配置的<snapshots><enabled>, <releases><enabled>以及其中的updatePolicy有关,如果enabledfalse,则就不存在下载指定类型的依赖这一说了,当命令设置-U参数如mvn clean install-U这时会忽略updatePolicy的设置
不要在发布的构件版本中声明release或者latest,这是不推荐的做法,因为基于更新策略maven会去检查仓库中最新的版本,这可能会因为代码接口的变动而导致项目构件失败

镜像mirror

如果仓库A能提供仓库B存储的所有内容,那么就可以认为AB的一个镜像,通俗的说,能从B中获取的构件那么一定也可以从A中获取
由于地理位置的原因,通常我们需要设置合理的镜像来代替中央仓库,比如可以设置aliyun镜像

<settings>
...
    <mirrors>
        <mirror>
            <id>aliyunmaven</id>
            <name>Aliyun Maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
            <mirrorOf>central</mirror>
        </mirror>
    </mirrors>
</settings>

上面通过配置aliyun镜像作为中央仓库的镜像,当访问中央仓库时,请求会被代理到镜像服务器上
关于镜像的一个更为常见的用法是结合私服,私服可以代理任何远程仓库,对于组织内部的人来说,使用一个私服就等于使用了所有需要的外部仓库,这时只需要在私服上进行配置,而项目中只需要简单的配置使用私服即可,这时私服就是所有远程仓库的镜像

镜像特殊语法
<mirrorOf>*</mirrorOf>:匹配所有远程仓库
<mirrorOf>external:*</mirrorOf>:匹配所有远程仓库,但是排除对本地仓库和系统依赖的访问
<mirrorOf>repo1,repo2</mirrorOf>:匹配多个仓库,用逗号隔开
<mirrorOf>*,!repo1</mirrorOf>:匹配所有仓库但排除repo1仓库
需要注意的是:镜像完全屏蔽了对指定仓库的访问,如果镜像仓库不稳定那么将会导致maven构建失败!这一点尤为重要

仓库搜索服务
我们需要在项目中引用各种各样的依赖,那么这些依赖的坐标具体是什么,我们可以从下面提供的两种搜索服务中获取到

  • Sonatype Nexus
    https://repository.sonatype.org/
test3.png
  • MVNrepository
    https://mvnrepository.com/(本人最常用)
test04.png

好了,篇幅有点长,关于仓库和镜像的知识点还是挺多的,需要慢慢巩固学习

推荐阅读更多精彩内容

  • 在Maven中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。 Maven在某个统一的位置存储所有项目的...
    三也视界阅读 900评论 0 2
  • 在Maven世界中,依赖、插件、项目构建完成后输出的jar包都可以看作是一个构件,任何一个构件都有一组坐标唯一标识...
    小菜鸟_Sonya阅读 111评论 0 0
  • 在 Maven 的术语中,仓库是一个位置(place)。Maven 仓库是项目中依赖的第三方库,这个库所在的位置叫...
    41uLove阅读 1,495评论 2 3
  • 五. 仓库 在Maven中,任何一个依赖、插件或者项目构建的输出,都可以称之为 构件。Maven在某个统一的位置存...
    wind_sky阅读 545评论 0 0
  • 首先私服是一种衍生出来的特殊的Maven远程仓库,构建私服的好处请看3.5私服 可以帮助大家建立私服的仓库管理软件...
    zlcook阅读 8,990评论 0 32