Maven Central 发布
→ 返回构建工具
将开源 Java 库发布到 Maven Central,使任何项目都能通过 groupId/artifactId/version 依赖。
发布渠道概览
历史(~2024 年 2 月前):
开发者 → OSSRH(Sonatype Nexus)→ Maven Central
需要申请 Sonatype Jira 账号,流程繁琐
现在(Central Portal,2024 年 2 月起):
开发者 → Central Portal(central.sonatype.com)→ Maven Central
直接在 Web 界面注册,更简洁
旧 OSSRH 账号仍可用,但新项目推荐用 Central Portal
两种渠道最终都发布到同一个 Maven Central,使用者不感知区别。
前置条件
1. Coordinates(坐标)命名规范
<groupId>io.github.your-username</groupId> <!-- 必须拥有该命名空间 -->
<artifactId>your-library</artifactId>
<version>1.0.0</version>groupId 命名空间:必须是你控制的域名反转,常见方式:
- 自有域名:
com.example(需验证 DNS TXT 记录) - GitHub:
io.github.your-username(通过 GitHub 账号验证) - GitLab:
io.gitlab.your-username
2. POM 必填元素
Maven Central 要求 POM 包含以下元素(否则发布被拒绝):
<project>
<groupId>io.github.your-username</groupId>
<artifactId>your-library</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<!-- 必填:项目名称、描述、URL -->
<name>Your Library</name>
<description>A brief description of what this library does.</description>
<url>https://github.com/your-username/your-library</url>
<!-- 必填:开源许可证 -->
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<!-- 必填:开发者信息 -->
<developers>
<developer>
<id>your-username</id>
<name>Your Name</name>
<email>you@example.com</email>
</developer>
</developers>
<!-- 必填:SCM(源码管理)信息 -->
<scm>
<connection>scm:git:git://github.com/your-username/your-library.git</connection>
<developerConnection>scm:git:ssh://github.com/your-username/your-library.git</developerConnection>
<url>https://github.com/your-username/your-library/tree/main</url>
</scm>
</project>3. 必须上传的文件
每个构件(jar/pom/sources/javadoc)都需要:
.jar本体-sources.jar(源码包)-javadoc.jar(文档包).pom文件- 上述每个文件的
.ascGPG 签名文件 - 上述每个文件的
.md5/.sha1校验和(Maven 插件自动生成)
GPG 签名配置
所有上传到 Maven Central 的构件必须有 GPG 签名。
→ 详细 GPG 操作见 OpenPGP
# 确认已有 GPG 密钥
gpg --list-keys
# 获取密钥 ID(用于 Maven/Gradle 配置)
gpg --list-keys --keyid-format SHORT
# 输出示例:pub ed25519/ABCD1234 2024-01-01
# 将公钥上传到密钥服务器(Maven Central 校验签名时需要)
gpg --keyserver hkps://keyserver.ubuntu.com --send-keys ABCD1234
gpg --keyserver hkps://keys.openpgp.org --send-keys ABCD1234注册 Central Portal
- 访问 central.sonatype.com → Sign In(用 GitHub 账号登录)
- View Namespaces → Add Namespace
- 输入 namespace(如
io.github.your-username) - 按提示完成验证:
- GitHub namespace:在 GitHub 创建指定名称的 public 仓库
- 自有域名:添加 DNS TXT 记录
- 等待验证通过(通常几分钟~1 小时)
Maven 配置(推荐方式)
pom.xml:添加必要插件
<build>
<plugins>
<!-- 生成 sources jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals><goal>jar-no-fork</goal></goals>
</execution>
</executions>
</plugin>
<!-- 生成 javadoc jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
<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>3.2.4</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals><goal>sign</goal></goals>
<configuration>
<!-- CI 环境下用 loopback 避免交互弹窗 -->
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</execution>
</executions>
</plugin>
<!-- Central Portal 发布插件(新版推荐)-->
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.5.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<!-- true = 自动发布;false = 上传后手动在 Portal 点击发布 -->
<autoPublish>false</autoPublish>
<waitUntil>validated</waitUntil>
</configuration>
</plugin>
</plugins>
</build>settings.xml:配置 Central Portal 凭据
<!-- ~/.m2/settings.xml -->
<servers>
<server>
<id>central</id>
<username><!-- Central Portal 生成的 Token username --></username>
<password><!-- Central Portal 生成的 Token password --></password>
</server>
</servers>生成 Token:Central Portal → Account → Generate User Token
执行发布
# 完整构建并发布到 Central Portal 暂存区
mvn clean deploy -P release
# 发布后在 Central Portal → Deployments 查看状态并手动 Publish
# 或配置 autoPublish=true 自动发布Gradle 配置
build.gradle.kts
plugins {
`java-library`
`maven-publish`
signing
id("com.gradleup.nmcp") version "0.0.9" // Central Portal 发布插件
}
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
pom {
name.set("Your Library")
description.set("A brief description.")
url.set("https://github.com/your-username/your-library")
licenses {
license {
name.set("Apache License, Version 2.0")
url.set("https://www.apache.org/licenses/LICENSE-2.0")
}
}
developers {
developer {
id.set("your-username")
name.set("Your Name")
email.set("you@example.com")
}
}
scm {
connection.set("scm:git:git://github.com/your-username/your-library.git")
developerConnection.set("scm:git:ssh://github.com/your-username/your-library.git")
url.set("https://github.com/your-username/your-library")
}
}
}
}
}
signing {
// CI 环境从环境变量读取密钥
val signingKey: String? by project
val signingPassword: String? by project
if (signingKey != null) {
useInMemoryPgpKeys(signingKey, signingPassword)
} else {
useGpgCmd() // 本地开发使用系统 GPG
}
sign(publishing.publications["mavenJava"])
}
nmcp {
// gradle.properties 或环境变量中读取
username.set(providers.gradleProperty("centralUsername"))
password.set(providers.gradleProperty("centralPassword"))
publicationType.set("USER_MANAGED") // 手动发布
// 或 "AUTOMATIC" 自动发布
}gradle.properties(本地,不提交到 git)
# ~/.gradle/gradle.properties
centralUsername=<Central Portal Token username>
centralPassword=<Central Portal Token password>执行发布
./gradlew publishAllPublicationsToNmcpRepository
# 然后去 Central Portal 手动 PublishGitHub Actions 自动发布
tag 推送时自动构建并发布到 Maven Central:
# .github/workflows/release.yml
name: Release to Maven Central
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
server-id: central
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
- name: Deploy to Maven Central
run: mvn --no-transfer-progress -B deploy -P release
env:
MAVEN_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}GitHub Secrets 配置:
| Secret 名称 | 内容 |
|---|---|
GPG_PRIVATE_KEY | gpg --armor --export-secret-keys KEY_ID 导出的私钥 |
GPG_PASSPHRASE | GPG 密钥密码 |
CENTRAL_USERNAME | Central Portal Token username |
CENTRAL_PASSWORD | Central Portal Token password |
# 导出 GPG 私钥用于 GitHub Secrets
gpg --armor --export-secret-keys your@email.com | cat版本规范
| 版本类型 | 格式示例 | 说明 |
|---|---|---|
| RELEASE | 1.0.0、1.2.3 | 正式发布版,发布后不可修改 |
| SNAPSHOT | 1.0.1-SNAPSHOT | 开发版,可重复发布覆盖,不进入 Maven Central |
SNAPSHOT 版本只能发布到私有仓库或 OSSRH SNAPSHOT 仓库,不能发布到 Maven Central 正式仓库。
发布检查清单
□ POM 包含必填字段(name/description/url/licenses/developers/scm)
□ 版本号为 RELEASE(不含 -SNAPSHOT)
□ GPG 密钥已上传到公共密钥服务器
□ 生成 sources jar(-sources.jar)
□ 生成 javadoc jar(-javadoc.jar)
□ 所有文件有 GPG 签名(.asc)
□ namespace 在 Central Portal 已验证
□ Central Portal Token 凭据已配置
□ 本地测试:mvn verify 通过
常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
Missing required POM elements | POM 缺少 name/description/url/licenses/developers/scm | 补充必填字段 |
GPG key not found on keyserver | 公钥未上传到密钥服务器 | gpg --send-keys KEY_ID |
Signature invalid | 签名文件与构件不匹配 | 重新构建并签名 |
Namespace not verified | groupId 对应命名空间未在 Portal 验证 | 完成 namespace 验证流程 |
Version already exists | RELEASE 版本已发布,不可覆盖 | 升版本号重新发布 |
javadoc errors | Javadoc 生成失败 | 修复 Javadoc 格式,或临时 -Djavadoc.failOnError=false |
OSSRH 旧流程(已注册账号的项目)
如果是 2024 年前注册的旧 OSSRH 账号,发布到 s01.oss.sonatype.org:
<!-- pom.xml distributionManagement -->
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<!-- 使用 nexus-staging-maven-plugin 代替 central-publishing-maven-plugin -->
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>