Maven05-maven仓库和nexus
1、Maven仓库介绍
1.1、何为Maven仓库
- 坐标和依赖是任何一个构件在Maven世界中的逻辑表示方式。而构件的物理表示方式是文件,Maven通过仓库来统一管理这些文件。
1.1、什么是构件
- 在Maven世界中,任何一个依赖、插件或者项目构建的输出,都可以称为构件。例如,依赖log4j-1.2.15.jar是一个构件,插件maven-compiler-plugin-2.0.2.jar是一个构件,mavenproject-A项目构建完成后的输出mavenproject-A-1.0-SNAPSHOT.jar也是一个构件。
- 任何一个构件在maven中都有一组坐标唯一标识。
2、为什么要使用仓库
- 在一台工作站上,可能会有几十个Maven项目,所有项目都会使用maven-compiler-plugin,大部分项目会用到了log4j,还有一小部分项目会用到了Spring Framework。
- 在不使用Maven的项目中,要在每个项目的lib/目录中存放该项目需要的构件,这样就会造成大量的重复,不仅会浪费磁盘空间,而且也难于统一管理。
- 在使用Maven的项目中,得益于坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目的构件,这个统一的位置就是仓库。实际的Maven项目将不再各自存储其依赖文件,它们只需要声明这些依赖的坐标,在需要的时候(例如,编译项目的时候需要将依赖加入到classpath中),Maven会自动根据坐标找到仓库中的构件,并使用它们。
- 为了实现重用,项目构建完毕后生成的构件也可以安装或者部署到仓库中,供其他项目使用。
1.2、maven仓库的布局
- 任何一个构件都有唯一的坐标,根据坐标可以定义其在仓库中的唯一存储路径,这便是Maven仓库的布局方式。例如,log4j:log4j:1.2.15这一依赖,其对应的仓库路径为log4j/log4j/1.2.15/log4j-1.2.15.jar,即路径与坐标的大致对应关系为groupId/artifactId/version/artifactId-version.packaging。
- 根据一个实际的例子来分析路径的生成,考虑这样一个构件:groupId=org.testng、artifactId=testng、version=5.8、classifier=jdk15、packaging=jar,其对应的路径按如下步骤生成:
- (1)基于构件的groupId准备路径,将groupId中的句点分隔符转换成路径分隔符。就是将示例中的groupId org.testng转换成org/testng,之后再加一个路径分隔符斜杠,这时路径就变成了org/testng/。
- (2)基于构件的artifactId准备路径,也就是在前面的基础上加上artifactId以及一个路径分隔符,这时路径就变成了org/testng/testng/
- (3)使用版本信息。在前面的基础上加上version和路径分隔符,这时路径就变成了org/testng/tesgng/5.8/。
- (4)然后依次加上artifactId,构件分隔符连字号,以及version,这时路径就变成了org/testng/testng/5.8/testng-5.8。
- 注意,这里使用了artifactId.getVersion()而上一步用的是artifactId.getBaseVersion(),baseVersion主要是为SNAPSHOT版本服务的,例如version为1.0-SNAPSHOT的构件,其baseVersion就是1.0。
- (5)如果构件有classifier,就加上构件分隔符和classifier,这时路径就变成了org/testng/testng/5.8/testng-5.8-jdk15。
- (6)检查构件的extension,若extension存在,则加上句点分隔符和extension,这时路径就变成了org/testng/testng/5.8/testng-5.8-jdk15.jar。
- 注意,extension是从artifactHandler而非antifact获取,artifactHandler是由项目的packaging决定的。因此,可以说,packaging决定了构件的扩展名。
- Maven仓库是基于简单文件系统存储的,理解了其存储方式,当遇到一些与仓库相关的问题时,可以很方便地查找相关文件,方便定位问题。例如,当Maven无法获得项目声明的依赖时,可以查看该依赖对应的文件在仓库中是否存在,如果不存在,查看是否有其他版本可用,等等。
2、maven仓库的类型
- Maven仓库分有两类:本地仓库和远程仓库。
- 当Maven根据坐标寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件之后,会下载到本地仓库然后使用。如果本地仓库和远程仓库都没有需要的构件,Maven就会报错。
- 每个用户只有一个本地仓库,但可以配置访问多个远程仓库。
- 远程仓库有三种:
- 中央仓库是Maven核心自带的远程仓库,它包含了绝大部分开源的构件。在默认配置下,当本地仓库没有Maven需要的构件的时候,它就会尝试从中央仓库下载。
- maven内置中央仓库:
- https://repo1.maven.org/maven2/
- https://repo.maven.apache.org/maven2/
- maven内置中央仓库:
- 私服是另一种特殊的远程仓库,为了节省带宽和时间,应该在局域网内架设一个私有的仓库服务器,用其代理所有外部的远程仓库,内部的项目还能部署到私服上供其他项目使用。
- 其他常见的公开的远程仓库:
- Java.net Maven仓库:https://maven.java.net/content/groups/public/
- JBoss Maven仓库:https://repository.jboss.org/nexus/content/groups/public/
- 阿里云 Maven仓库配置页:https://developer.aliyun.com/mvn/guide
- 搜素构件(获取构件坐标):https://mvnrepository.com/
- 中央仓库是Maven核心自带的远程仓库,它包含了绝大部分开源的构件。在默认配置下,当本地仓库没有Maven需要的构件的时候,它就会尝试从中央仓库下载。
- maven仓库的分类如图所示:
2.1、本地仓库
- 默认情况下,Maven的本地仓库是${HOME}/.m2/repository目录(不管是在Windows还是Linux上)。
- 注意,安装好Maven后,如果不执行任何Maven命令,本地仓库目录是不存在的。当用户执行第一条Maven命令之后,Maven才会创建本地仓库。
- 自定义Maven本地仓库,修改maven配置文件${M2_HOME}/conf/settings.xml的localRepository元素的值:
<settings> <!-- 将本地仓库设置为D:\maven-repo --> <localRepository>D:\maven-repo</localRepository> </settings>
- 一个构件只有在本地仓库中之后,才能由其他Maven项目使用。那么构件如何进入到本地仓库中呢?
- 最常见的是Maven自动将依赖从远程仓库下载到本地仓库中。
- 还有一种常见的情况是,将本地项目的构件安装到Maven仓库中。例如,本地有两个项目A和B,两者都无法从远程仓库获得,而同时A又依赖于B,为了能构建A,必须首先构建B并安装到本地仓库中(mvn clean install,Install插件的install目标会将项目构建输出的文件安装到本地仓库)。
2.2、中央仓库
- 由于最原始的本地仓库是空的,Maven必须知道至少一个可用的远程仓库,才能在执行Maven命令的时候下载到需要的构件。中央仓库就是默认的远程仓库,Maven的安装文件自带了中央仓库的配置。
- 可以使用解压工具打开${M2_HOME}/lib/maven-model-builder-3.6.1.jar,然后找到超级POM文件org/apache/maven/model/pom-4.0.0.xml(该文件是所有Maven项目都会继承的超级POM),可以看到如下的配置:
- 这段配置使用id central对中央仓库进行唯一标识,其名称为Central Repository,它使用default仓库布局(对于Maven1的仓库,需要配置layout为legacy)。最后需要注意的是snapshots元素,其子元素enabled的值为false,表示不从该中央仓库下载快照版本的构件
<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>
- 中央仓库包含了这个世界上绝大多数流行的开源Java构件,以及源码、作者信息、SCM、信息、许可证信息等,每个月这里都会接受全世界Java程序员大概1亿次的访问。由于中央仓库包含了超过2000个开源项目的构件,因此,一般来说,一个简单Maven项目所需要的依赖构件都能从中央仓库下载到。这也解释了为什么Maven能做到“开箱即用”。
2.3、私服
- 私服是一种特殊的远程仓库,它是部署在局域网内的Maven仓库,代理广域网上的远程仓库,供局域网内的Maven用户使用。
- 当Maven需要下载构件的时候,它会先请求私服,如果私服上不存在该构件,私服就会从外部的远程仓库下载,并缓存在私服上,之后再为Maven的下载请求提供服务。
- 此外,一些无法从外部远程仓库下载到的构件也能从本地上传到私服上供大家使用。
- 私服的作用:
- 节省自己的外网带宽。私服可以减少大量的对于外部远程仓库的重复请求。
- 加速Maven构建。不停地连接请求外部远程仓库是十分耗时的,并且Maven的一些内部机制(如快照更新检查)要求Maven在执行构建的时候不停地检查远程仓库数据。因此,当项目配置了很多外部远程仓库的时候,构建的速度会被大大降低。使用私服可以很好地解决这一问题,因为Maven只需要检查局域网内私服的数据。
- 部署第三方构件。例如组织内部生成的私有构件、Oracle的JDBC驱动由于版权因素不能发布到公共仓库中。建立私服之后,便可以将这些构件部署到这个内部的仓库中,供内部的Maven项目使用。
- 提高稳定性,增强控制。Maven构建高度依赖远程仓库,因此,当Internet不稳定的时候,Maven构建也会变得不稳定,甚至无法构建。使用私服后,即使暂时没有Internet连接,由于私服中已经缓存了大量构件,Maven也仍然可以正常运行。此外,一些私服软件(如Nexus)还提供了很多额外的功能,如权限管理、RELEASE/SNAPSHOT区分等,管理员可以对仓库进行一些更高级的控制。
- 降低中央仓库的负荷。运行并维护一个中央仓库不是一件容易的事情,服务数百万的请求,存储数T的数据,需要相当大的财力。使用私服可以避免很多对中央仓库重复的下载,想象一下,一个有数百位开发人员的公司,在不使用私服的情况下,一个构件往往会被重复下载数百次;建立私服之后,这几百次下载就只会发生在内网范围内,私服对于中央仓库只有一次下载。
3、配置访问远程仓库
3.1、添加一个远程仓库
- 在很多情况下,默认的中央仓库无法满足项目的需求,可能项目需要的构件存在于另外一个远程仓库中,如JBoss Maven仓库。这时,可以在pom.xml中配置该仓库:
<project> ... <repositories> <repository> <id>jboss</id> <name>JBoss Repository</name> <url>https://repository.jboss.org/nexus/content/groups/public/</url> <releases> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <enabled>false</enabled> </snapshots> <layout>default</layout> </repository> </repositories> ... </project>
- 在repositories元素下,可以使用repository子元素声明一个或者多个远程仓库。
- 声明了一个id为jboss,名称为JBoss Repository的仓库。任何一个仓库声明的id必须是唯一的,尤其需要注意的是,Maven自带的中央仓库使用的id为central,如果其他的仓库声明使用了该id,就会覆盖中央仓库的配置。
- url值指向远程仓库的地址,一般来说,该地址都基于http协议,Maven用户可以在浏览器中打开仓库地址浏览构件。
- releases和snapshots用来控制Maven对于发布版构件和快照版构件的下载。
- enabled用来控制是否可以下载对应版本的构件。如releases的enabled值为true,表示开启JBoss仓库的发布版本下载支持,而snapshots的enabled值为false,表示关闭JBoss仓库的快照版本的下载支持。因此,根据该配置,Maven只会从JBoss仓库下载发布版的构件,而不会下载快照版的构件。
- updatePolicy用来配置Maven从远程仓库检查更新的频率(即更新策略),默认的值是daily,表示Maven每天检查一次。其他可用的值包括:never(从不检查更新)、always(每次构建都检查更新)、interval:X(每隔X分钟检查一次更新(X为任意整数))。
- 用户可以再命令行使用参数-U,强制检查更新(这时Maven就会忽略<updatePolicy>的配置)。
- checksumPolicy用来配置Maven检查检验和文件的策略。当构件被部署到Maven仓库中时,会同时部署对应的校验和文件。在下载构件的时候,Maven会验证校验和文件,如果校验和验证失败,怎么办?当checksumPolicy的值为默认的warn时,Maven会在执行构建时输出警告信息,其他可用的值包括:fail(Maven遇到校验和错误就让构建失败)、ignore(使Maven完全忽略校验和错误)。
- layout值default表示仓库的布局是Maven2及Maven3的默认布局,而不是Maven1的布局。
3.2、将构件部署到远程仓库
- 私服的一大作用是部署第三方构件,包括组织内部生成的构件以及一些无法从外部仓库直接获取的构件。无论是日常开发中生成的构件,还是正式版本发布的构件,都需要部署到仓库中,供其他团队成员使用。
- Maven不但可以对项目进行编译、测试、打包,还可以将项目生成的构建部署到仓库中。
- (1)首先,需要编辑项目的pom.xml文件,配置distributionManagement元素。
- distributionManagement包含repository和snapshotRepository两个子元素,前者表示发布版本构件的仓库,后者表示快照版本的仓库。这两个元素下都需要配置id、name和url,id是远程仓库的唯一标识,name是为了方便人阅读,url是远程仓库的地址。
<project> ... <distributionManagement> <repository> <id>my-proj</id> <name>Project Release Repository</name> <url>http://192.168.1.100/content/repo/proj-releases</url> </repository> <snapshotRepository> <id>my-proj</id> <name>Project Snapshot Repository</name> <url>http://192.168.1.100/content/repo/proj-snapshot</url> </snapshotRepository> </distributionManagement> ... </project>
- (2)配置正确后,在命令行运行mvn clean deploy,Maven就会将项目构建输出的构件部署到配置对应的远程仓库。如果项目当前的版本是快照版本,就会部署到快照版本的远程仓库,否则就会部署到发布版本的远程仓库。
3.3、远程仓库的认证
- 大部分远程仓库无须认证就可以访问并下载构件(注意,部署构件时,通常需要认证),但有的出于安全方面的考虑,需要提供认证信息才能访问。例如,组织内部有一个Mayen仓库服务器,该服务器为每个项目都提供独立的Maven仓库,为了防止非法的仓库访问,管理员为每个仓库提供了一组用户名及密码。这时,为了能Maven访问仓库,就需要配置认证信息。
- 不管是从远程仓库下载构件,还是部署构件至远程仓库,当需要认证的时候,配置的方式是一样的。
- 配置认证信息和配置仓库信息不同,仓库信息可以直接配置在pom.xml文件中,但是认证信息必须配置在settings.xml文件中。这是因为pom.xml会被提交到代码仓库中供所有成员访问的,而settings.xml一般只放在本地。因此,在settings.xml中配置认证信息更为安全。
- 假设为一个id为my-proj的仓库配置认证信息,在settings.xml文件中添加如下内容:
- Maven使用settings.xml文件中的servers元素及其server子元素配置仓库认证信息。该仓库的认证用户名是repo-user,认证密码是repo-pwd。
- 这里需要注意的是settings.xml中server元素的id必须与pom.xml中需要认证的repository元素的id完全一致。换句话说,正是这个id将认证信息与仓库配置联系在了一起。
<settings> ... <servers> <server> <id>my-proj</id> <username>repo-user</username> <password>repo-pwd</password> </server> </servers> ... </settings>
4、解析依赖
- 仓库元数据并不是永远正确的,有时候当用户发现无法解析某些构件,或者解析得到错误构件的时候,就有可能是出现了仓库元数据错误,这时就需要手工地,或者使用工具(如Nexus)对其进行修复。
4.1、快照版本
- Maven为什么要区分发布版和快照版呢?
- 例如,张三在开发模块A的2.1版本,该版本还未正式发布,与模块A一同开发的还有模块B,它由张三的同事李四开发,B的功能依赖于A。在开发的过程中,张三需要经常将自己最新的构建输出交给李四,供他开发和集成调试。有了快照版本机制后:
- 张三只需要将模块A的版本设定为2.1-SNAPSHOT,然后部署到私服中。在部署到私服的过程中,Maven会自动为该构件打上时间戳。比如2.1-20091214.221414-13就表示2009年12月14日22点14分14秒的第13次快照。有了该时间戳,Maven就能随时找到仓库中该构件2.1-SNAPSHOT版本最新的文件。
- 李四只需要配置依赖模块A的2.1-SNAPSHOT版本即可,当他构建模块B的时候,Maven会自动从仓库中检查模块A的2.1-SNAPSHOT的最新构件,当发现有更新时便进行下载。默认情况下,Maven每天检查一次更新(由仓库配置的updatePolicy控制),用户也可以使用命令行-U参数强制让Maven检查更新,如mvn clean install -U。
- 快照版本只应该在组织内部的项目或模块间依赖使用,因为这时,组织对于这些快照版本的依赖具有完全的理解及控制权。项目不应该依赖于任何组织外部的快照版本依赖,由于快照版本的不稳定性,这样的依赖会造成潜在的危险。也就是说,即使项目构建今天是成功的,由于外部的快照版本依赖实际对应的构件随时可能变化,项目的构建就可能由于这些外部的不受控制的因素而失败。
4.2、从仓库解析依赖的机制
- 当本地仓库没有被依赖的构件时,Maven会自动从远程仓库下载。当依赖的版本是快照版本时,Maven会自动找到最新的快照版本。这背后的依赖解析机制可以概括如下:
- (1)如果依赖的依赖范围是system,Maven会直接从本地文件系统解析构件。
- (2)如果依赖的依赖范围不是system,可能会出现三种情况:
- (1)依赖的版本是显式的发布版本,Maven会根据依赖坐标计算仓库路径,然后会直接查找本地仓库。
- (1)如果本地仓库存在相应的构件,则解析成功。
- (2)如果本地仓库不存在相应的构件,就会查找所有的远程仓库,发现后,下载并解析使用。
- (2)如果依赖的版本是RELEASE或者LATEST,会基于更新策略读取所有远程仓库的元数据groupId/artifactId/maven-metadata.xml,将其与本地仓库的对应元数据合并后,计算出RELEASE或者LATEST的值,然后基于这个值查找本地和远程仓库。
- RELEASE和LATEST分别最新发布版本和最新版本(包含快照)。
- 注意,不推荐在依赖声明中使用LATEST和RELEASE,因为Maven随时都可能解析到不同的构件,可能今天LATEST是1.3.6,明天就成为1.4.0-SNAPSHOT了,且Maven不会明确告诉用户这样的变化。当这种变化造成构建失败的时候,发现问题会变得比较困难。
- 注意,Maven3不再支持在插件配置中使用LATEST和RELEASE。如果不设置插件版本,其效果就和RELEASE一样,Maven只会解析最新的发布版本构件。
- (3)如果依赖的版本是SNAPSHOT,也会基于更新策略读取所有远程仓库的元数据groupId/artifactId/version/maven-metadata.xml,将其与本地仓库的对应元数据合并后,得到最新快照版本的值,然后基于该值检查本地仓库,或者从远程仓库下载。
- 如果最后解析得到的构件版本是时间戳格式的快照,如1.4.1-20091104.121450-121,则复制其时间戳格式的文件至非时间戳格式,如SNAPSHOT,并使用该非时间戳格式的构件。
- (1)依赖的版本是显式的发布版本,Maven会根据依赖坐标计算仓库路径,然后会直接查找本地仓库。
- 示例,远程仓库中的groupId/artifactId/maven-metadata.xml文件列出了该仓库中存在的所有该构件的可用版本,同时latest元素指向了最新的版本4.13.2-SNAPSHOT,release元素指向了最新的发布版本4.13.1。
<?xml version="1.0" encoding="UTF-8"?> <metadata> <groupId>junit</groupId> <artifactId>junit</artifactId> <versioning> <latest>4.13.2-SNAPSHOT</latest> <release>4.13.1</release> <versions> ... <version>4.13-beta-3</version> <version>4.13-rc-1</version> <version>4.13-rc-2</version> <version>4.13</version> <version>4.13.1</version> <version>4.13.2-SNAPSHOT</version> </versions> <lastUpdated>20210213164433</lastUpdated> </versioning> </metadata>
- 示例,远程仓库中的groupId/artifactId/version/maven-metadata.xml文件的snapshot元素包含了timestamp和buildNumber两个子元素,分别代表这个快照的时间戳和构建号,基于这两个元素可以得到该仓库中该快照的最新构件版本实际为1.4.2-20091214.221414-13。
<?xml version="1.0" encoding="UTF-8"?> <metadata modelVersion="1.1.0"> <groupId>org.sonatype.nexus</groupId> <artifactId>nexus-indexer</artifactId> <version>1.4.2-SNAPSHOT</version> <versioning> <snapshot> <timestamp>20091214.221414</timestamp> <buildNumber>13</buildNumber> </snapshot> <lastUpdated>20091214052043</lastUpdated> </versioning> </metadata>
5、镜像
- 如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。换句话说,任何一个可以从仓库Y获得的构件,都能够从它的镜像中获取。
- 配置镜像仓库,编辑settings.xml文件的mirrors段:
- 在mirrors元素下,可以使用mirror子元素声明一个或者多个镜像仓库。
- id、name、url与配置远程仓库一样,表示该镜像仓库的唯一标识符、名称和地址。
- mirrorOf是原来仓库的ID。
- <mirrorOf>*</mirrorOf>:匹配所有远程仓库。
- <mirrorOf>external:*</mirrorOf>:匹配所有远程仓库,但使用localhost和使用file://协议的除外。也就是,匹配所有不在本机上的远程仓库。
- <mirrorOf>repo1, repo2</mirrorOf>:匹配repo1和repo2远程仓库。使用逗号分隔多个远程仓库。
- <mirrorOf>*,!repo1</mirrorOf>:匹配所有远程仓库,但repo1除外。使用感叹号将仓库从匹配中排除。
- 在mirrors元素下,可以使用mirror子元素声明一个或者多个镜像仓库。
- 注意,镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或者停止服务的时候,Maven仍将无法访问被镜像仓库,因而将无法下载构件。
- 示例,<mirrorOf>的值为central,表示该配置为Maven中央仓库的镜像(maven中央仓库的id是central),任何对于中央仓库的请求都会转至该镜像,用户也可以使用同样的方法配置其他仓库的镜像。
<settings> ... <mirrors> <mirror> <id>maven.aliyun.com</id> <name>Maven Central Repository Of Mirror</name> <url>https://maven.aliyun.com/repository/central</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors> ... </settings>
- 镜像的常见用法是与私服结合。由于私服可以代理任何外部的公共仓库(包括中央仓库),因此,对于组织内部的Maven用户来说,使用一个私服地址就等于使用了所有需要的外部仓库,这可以将配置集中到私服,从而简化Maven本身的配置。在这种情况下,任何需要的构件都可以从私服获得,私服就是所有仓库的镜像。
- 示例,<mirrorOf>的值是*星号,表示该配置是所有Maven仓库的镜像,任何对于远程仓库的请求都会被转至http://192.168.1.100/maven2/。如果该镜像仓库需要认证,则配置一个id为intenal-repository的<server>即可
<settings> ... <mirrors> <mirror> <id>intenal-repository</id> <name>Intenal Repository Manager</name> <url>https://192.168.1.100/maven2/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors> ... </settings>
6、使用Nexus创建私服
- 见:《安装Nexus及简单使用》https://www.cnblogs.com/maiblogs/p/17763574.html
1
# #