0x00 前言
Maven 中央仓库是由 Maven 社区提供的仓库,其中包含了包含了绝大多数流行的开源Java构件、以及源码、作者信息、SCM、信息、许可证信息等。
一般来说,简单的 Java 项目依赖的构件都可以在这里下载到。
国内大部分知名私服镜像仓库(如阿里云),都会代理到 Maven 中央仓库,以其作为主要的源库。
Maven 中央仓库提供了 3 个主要的站点:
- Sonatype JIRA: https://issues.sonatype.org/ 用于首次申请帐号和 GroupId
- Sonatype Nexus: https://s01.oss.sonatype.org/ 用于发布/存储/检索 JAR (含快照)
- Maven Repository: https://mvnrepository.com/ 用于检索已发布的 JAR (不含快照)
平时只会用到最后一个站点 Maven Repository,但如果要发布 JAR 到中央仓库,必须要用到所有站点。
0x10 发布流程
核心发布流程为:
- 注册 Sonatype 帐号
- 申请 GroupId
- 发布 GPG 公钥
- Maven 环境配置
- 对 JAR 进行 GPG 签名,并上传到 Maven 中央仓库
0x20 注册 Sonatype 帐号
打开 Sonatype JIRA 注册 Sonatype 的帐号。
这个帐号是 Sonatype JIRA 和 Sonatype Nexus 通用的,所以后面上传 JAR 到 Sonatype Nexus 会用到,切勿丢失。
0x30 预操作:申请 GroupId
0x31 填写 Jira Issue 工单
首先需要在 Sonatype JIRA 上面新建 Jira Issue 工单:
可以参考下面说明填写 Jira Issue 工单:
- 项目: 固定选
Community Support - Open Source Project Repository Hosting (OSSRH)
- 问题类型: 固定选
New Project
- 概要: 随便填一些英文即可,这个 项目/分类 下都是机器人在自动处理,不会关注这个字段
- 描述: 填写
Apply for a Group ID
,其实随便填即可,这个 项目/分类 下都是机器人在自动处理,不会关注这个字段 - Group Id: 填写需要申请的 GroupId,下文会详细说明
- Project URL: 填写 Maven 工程的 Github 仓库路径(如果还没上传代码需要先上传),例如 https://github.com/EXP-Codes/exp-libs-refactor
- SCM url: 与 Project URL 一致即可
- Username(s): 留空即可,无需填写
- Already Synced to Central: 选择
No
GroupId 是域名的倒装,域名与 GroupId 的对应关系可参考下表:
域名类型 | 个人域名 | Github 帐号域名 |
---|---|---|
域名 | ${DOMAIN}.com |
github.com/${ACCOUNT} |
GroupId | com.${DOMAIN} |
com.github.${ACCOUNT} |
域名(举例) | exp-blog.com | github.com/lyy289065406 |
GroupId(举例) | com.exp-blog | com.github.lyy289065406 |
若想申请个人域名,这里推荐到最便宜的域名服务商 namesilo 购买的域名,只需要 0.99 美刀/年,而且提供免费的域名解析服务。
如果没有个人域名、但代码在 Github 上,可以使用 Github 的帐号域名,Github 帐号域名是免费的,
完整的申请工单内容可以参考这里:
0x32 证明域名所有权
提工单之后,大约等 5 分钟,机器人会回复 Jira Issue 工单,要求你先证明域名的所有权:
根据提示操作即可(其实就是在域名解析到的站点中,添加指向此 Jira Issue 工单的信息而已)。
如果没问题,大概等半小时后,就能收到 GroupId 申请通过的回复:
GroupId 与 Maven 工程、甚至域名都并非强绑定关系,如工程 A 已申请了 GroupId,尔后工程 B 也需要发布到 Maven 中央仓库,都可以共用一个 GroupId。
0x40 预操作:发布 GPG 公钥
GnuPG (GNU Privacy Guard) 即 GPG,是一个使用 RSA 算法的密码学软件,用于加密、签名通信内容及管理非对称密码学的密钥。
GPG 在公网有提供自己的加解密服务,故 Sonatype 要求用户:
- 生成 GPG 密钥对
- 上传公钥到 GPG 的密钥服务器
- 用私钥加密 Jar 包再上传到 Sonatype
只有 Sonatype 在 GPG 的密钥服务器中找到对应的公钥、解密成功、并验证其中的用户信息后,才能真正发布 Jar 。
详细方法参考《GnuPG 环境安装与配置》,过程中要存储密钥对的保护密码,下面填 passphrase 需要用到。
需要注意的是,在 Linux 环境下,执行 Maven 命令后、在输入 GPG 保护密码前,可能会报错 GPG 签名失败:
Failed to execute goal org.apache.maven.plugins:maven-gpg-plugin:1.6:sign
gpg: signing failed: Inappropriate ioctl for device
原因是,默认情况下 GPG 的密码是需要通过交互式窗口输入的,但是当前终端的默认配置不支持窗口交互,导致报错。
解决方法也很简单:执行命令 export GPG_TTY=$(tty)
后,再重新执行 Maven 命令即可。
为了一劳永逸,可以把此命令写在环境变量文件
~/.bash
中。
0x50 Maven 环境配置
0x51 修改全局 setting.xml
在 Maven 配置文件 ~/.m2/setting.xml
的 <servers>
节点下添加以下 <server>
。
其中 <id>
需要和 <distributionManagement>
节点的 <id>
一致。
<settings>
... ...
<servers>
... ...
<server>
<id>sonatype</id> <!-- 与需要发布 Jar 的 Maven 工程 pom.xml 中 distributionManagement 节点的 id 一致 -->
<username>${SONATYPE_USERNAME}</username> <!-- 前面在 0x20 节 Sonatype JIRA 中注册的帐号 -->
<password>${SONATYPE_PASSWORD}</password> <!-- 前面在 0x20 节 Sonatype JIRA 中注册的密码 -->
<passphrase>${GPG_PASSWORD}</passphrase> <!-- 前面在 0x43 节生成 GPG 密钥对的保护密码 -->
</server>
... ...
</servers>
... ...
</settings>
0x52 修改工程 pom.xml
完整的配置可参考开源工程 exp-libs-refactor 的 pom.xml
首先需要添加发布 Jar 的基础信息:
<project>
... ...
<!-- License: 根据源码仓库的 License 自行修改 -->
<licenses>
<license>
<name>The Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<!-- 开发者信息: 与 0x43 节生成 GPG 密钥 对时填写的信息一致 -->
<developers>
<developer>
<name>EXP</name>
<email>exp.lqb@foxmail.com</email>
</developer>
</developers>
<!-- 工程信息: 对应内容修改为 0x31 节申请 GroupId 时填写的 SCM url 一致即可 -->
<scm>
<connection>scm:git:${SCM_URL}</connection>
<developerConnection>scm:git:${SCM_URL}</developerConnection>
<url>${SCM_URL}</url>
</scm>
<!-- 项目信息: 根据实际情况修改即可 -->
<name>${PROJECT_NAME}</name>
<description>${PROJECT_DESC}</description>
<url>${SCM_URL}</url>
<groupId>${GROUP_ID}</groupId> <!-- 0x30 节申请的 GroupId -->
<artifactId>${PROJECT_NAME}</artifactId>
<version>${PROJECT_VERSION}</version>
... ...
</project>
其次需要添加发布到 Sonatype 时用到的插件,但为了避免影响原有插件,可以添加一个 <profile>
在其中指定这些插件:
- maven-compiler-plugin: 编译 maven 工程
- maven-source-plugin: 生成源码 jar 包
- maven-javadoc-plugin: 生成 javadoc 包
- maven-gpg-plugin: 对 Jar 进行GPG 签名
- nexus-staging-maven-plugin: 发布 Jar 到 Sonatype
大部分插件配置都无需修改,除了 nexus-staging-maven-plugin 插件的 <serverId>
需要和 <distributionManagement>
节点的 <id>
一致。
<project>
... ...
<profiles>
... ...
<profile>
<!-- mvn 命令执行时指定 -P ttyForDeploy 即可优先使用此 profile 的配置 -->
<id>ttyForDeploy</id>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<!-- 编译 maven 工程 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!-- 生成源码 jar 包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 生成 javadoc 包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<charset>${project.build.sourceEncoding}</charset>
<encoding>${project.build.sourceEncoding}</encoding>
<docencoding>${project.build.sourceEncoding}</docencoding>
<!-- 从 JDK8 开始, Javadoc 中添加了 doclint, 目的是旨在获得符合 W3C HTML 4.01 标准规范的 HTML 文档 -->
<!-- 简而言之 Javadoc 不允许出现 html 相关的元素, 若旧注释含有这些元素又不想修改, 只能关闭 doclint -->
<additionalOptions>
<additionalOption>-Xdoclint:none</additionalOption>
</additionalOptions>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- GPG 签名 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<!-- 默认签名时通过交互方式输入 gpg 密码(若 linux 需终端支持) -->
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 分发管理和认证:用于部署和发布到中央仓库 https://mvnrepository.com/ -->
<!-- 此插件对于发布 SNAPSHOT 版本时不会触发 -->
<!-- 其作用是自动在 https://s01.oss.sonatype.org/ 的 Staging Repositories 中 close 并 release,无需手动干预 -->
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<extensions>true</extensions>
<configuration>
<serverId>sonatype</serverId> <!-- 要与 distributionManagement 定义的 id 一致 -->
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
</plugins>
</build>
</profile>
... ...
<profiles>
... ...
<project>
最后配置 <distributionManagement>
指向 Sonatype Nexus 仓库即可。
其中 <id>
要与上文提到的所有相关 id 一致。
<project>
... ...
<distributionManagement>
<!-- sonatype 的快照仓库 -->
<snapshotRepository>
<id>sonatype</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<!-- sonatype 的正式仓库 -->
<repository>
<id>sonatype</id>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
... ...
<project>
0x60 签名并发布 Jar
前面环境都准备好后,在 Maven 工程根目录执行命令:
mvn clean deploy -P ttyForDeploy
即可自动把工程打成 Jar 、然后对其签名、并发布到 Maven 中央仓库。
签名过程中会弹出交互窗口要求输入 GPG 密钥的密码,填写前面 GPG 的密码的即可:
命令执行完成后,大概等 2 小时,可以在 Sonatype Nexus 和 Maven Repository 查到,其他工程输入其坐标即可使用。