搭建 Nexus 私服,并从一个 Android 开发者的角度去上传依赖到 Nexus 私服。
前言
这是一篇旨到弄清以下问题的博客:
- Maven 仓库分类?
- Nexus 仓库是什么?
- Nexus 和 Maven 的关系?
- 为什么要创建 Nexus 仓库?
- 如何搭建 Nexus 仓库?
- 为什么仓库 release 和 snapshot 之分?
- snapshot 仓库要怎么使用?
我不知道也未曾料想过,我会从一个 gradle 依赖引用的 @
符号开始,思路分散的如此的地步,我的脑子是个神奇的东西。
Maven 的仓库分类
从 Maven 的依赖下载管理角度来看,Maven 仓库的可分为两类,远程仓库和本地仓库。
本地仓库是电脑硬盘上的一个目录,远程仓库是在指放在远程服务器上电脑硬盘上的 Maven 仓库目录,使用需添加远程仓库的地址,才能正常连接下载依赖。
Maven 的远程仓库分为中央仓库和私服仓库。中央仓库存放了世界各地用户上传的依赖包,比较出名的是 JCenter 和 Maven Central,开源的第三方依赖一般都会上传到这两个中央仓库,这样我们只用添加这两个中央仓库的链接地址,就可以下载各种我们需要的依赖了。
由于每次构建都需要从中央仓库下载依赖,对于中央仓库的服务器来说,压力很大,间接导致下载速度很慢。并且,上传到中央仓库的依赖都是开源的,只要知道名字,用户就都能下载,虽然开源精神是值得倡导的,但是一些公司内部的核心依赖包,出于一些考虑,无法直接开源。于是,私服仓库就诞生了。在公司的局域网,搭建一个 Nexus 仓库,把公司内部不想开源的依赖包上传到私服仓库中,这样下载依赖的需满足两个条件,私服仓库的地址和在公司局域网内,一定程度上有个保密和安全性。
私服是一种特殊的远程仓库
私服是一种特殊的远程仓库, 它设在局域网内, 通过代理广域网上的远程仓库, 供局域网内的 Maven 用户使用。私服的存在,特殊于,它改变了 Maven 下载依赖的机制。
只有本地仓库和远程中央仓库时,当 Maven 根据坐标寻找依赖包时, 首先会检索本地仓库, 如果本地存在则直接使用, 否则去远程仓库下载.
但是,当有了一个私服后:
当检索本地仓库发现不存在的时候, Maven 客户端先向私服请求, 如果私服不存在该依赖包, 则从外部的远程仓库下载, 并缓存在私服上, 再为客户提供下载服务。简单的理解是私服相当于本地仓库和远程中央仓库的中间缓存,因此,私服的存在,可以节省公网带宽,利用内网下载依赖项速度快。
下面是一张本地仓库,私服仓库和远程中央仓库的依赖下载示意图(构件可理解为三方的依赖包,是比较专业的说法):
Nexus 搭建私服
有三种比较流行的 Maven 仓库管理软件可以创建私服,Apache基金会的 Archiva,JFrog 的 Artifactory ,Sonatypec的 Nexus,本博客的内容是使用 Nexus 搭建私服。
Nexus 的话说的很霸气,自称为 “世界上第一个也是唯一可以免费使用的通用储存库解决方案”。
Nexus 的 2.x 和 3.x 版本改动很多,我写下这篇博客的时候,Nexus 最新版本是 3.8.0,因此按照 3.8.0 记录。
Java 环境准备
当前 Sonatype Nexus 基于 Java,要求 Java 8 Runtime Environment(JRE),java 版本不能低于 1.8.0,所以请先自行下载 JDK 最新版本,并配好 Java 环境。
想确认已安装的 Java 版本,可以运行 java -version
命令查看。
下载 Nexus
去 官方网站 下载自己对应平台的 Nexus 安装包。
运行 Nexus
将下载的压缩包,解压,会看到如下目录:
进入 bin 目录,启动 Nexus 。
1 | // Windows小伙伴们用这个 |
//上传构件的信息
GROUP=com.dr
VERSION_NAME=1.0.3
POM_ARTIFACT_ID=nexuslibrary
//上传的目标仓库地址
SNAPSHOT_REPOSITORY_URL=http://localhost:8081/repository/maven-snapshots/
RELEASE_REPOSITORY_URL=http://localhost:8081/repository/maven-releases/
//Nexus 的私服的用户名称和密码
NEXUS_USERNAME=admin
NEXUS_PASSWORD=admin123
1 |
|
apply plugin: ‘maven’
def isReleaseBuild() {
return VERSION_NAME.toUpperCase().contains(“SNAPSHOT”) == false
}
def getRepositoryUsername() {
return hasProperty(‘NEXUS_USERNAME’) ? NEXUS_USERNAME : “”
}
def getRepositoryPassword() {
return hasProperty(‘NEXUS_PASSWORD’) ? NEXUS_PASSWORD : “”
}
def getRepositoryUrl() {
return isReleaseBuild() ? RELEASE_REPOSITORY_URL : SNAPSHOT_REPOSITORY_URL
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: getRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
}
}
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = ‘javadoc’
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = ‘sources’
from android.sourceSets.main.java.sourceFiles
}
//解决 JavaDoc 中文注释生成失败的问题
tasks.withType(Javadoc) {
options.addStringOption(‘Xdoclint:none’, ‘-quiet’)
options.addStringOption(‘encoding’, ‘UTF-8’)
options.addStringOption(‘charSet’, ‘UTF-8’)
}
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
}
1 |
|
apply from: ‘upload_nexus.gradle’
1 |
|
allprojects {
repositories {
//…
//添加本地仓库地址
maven { url ‘http://localhost:8081/repository/maven-public/‘ }
}
}
1 |
|
dependencies {
//…
compile ‘com.dr:nexuslibrary:1.0.0’
}
1 |
|
//Windows小伙伴们用这个
gradlew build –refresh-dependencies
//Mac小伙伴们用这个
./gradlew build –refresh-dependencies
```
结语
以上是本博客的全部内容,博客测试用的 Demo 我放在了 GitHub 上,地址在此:DR_MavenDemo,有问题欢迎提 issue。
目前来看,我脑洞突如其来的 Maven 系列,到此算是告一段落了,一系列下来,触到的几乎都是我的知识盲点,每当我开始不知所谓的想飘飘然时,时刻提醒自己,我还只是一井蛙。
最后,年后的第一篇博客,祝大家,狗年快乐呢~
参考博客
刚刚开通了个人微信公众号,最新的博客,好玩的事情,都会在上面分享,欢迎关注,让我们一起学习和成长。