Maven Central发布指南:将你的Jar/War包发布到Maven中央仓库

1,530 阅读5分钟

序言

最近收到Sonatype的迁移提醒邮件,内容大意为:原有的OSSRH发布模式将在2025年6月30日停止维护,之后原有的OSSRH发布方式将不再适用,需要尽快迁移到Maven Central。如果你之前使用过OSSRH但是没有使用过Maven Central发布Jar包、或者想要从0开始学习如何将Jar包发布到中央仓库,都可以通过本文章学习。本文章将会从0开始,一步一步引导你如何将Jar包发布到Maven Central中央仓库。

image.png

文章目录

  1. Maven仓库账号准备
  2. GPG密钥工具准备
  3. 部署流程

1. Maven仓库账号准备

Tips:之前在OSSRH上发布过Jar/War包的,可以使用已有的账号,迁移更方便

1.1. Sonatype账号注册和登录

打开 Maven Contral ,找到右上角的「Sign in」进行注册或登录

image.png image.png

1.2. 创建/迁移命名空间

登录之后,还是右上角,找到「View Namespaces」

image.png

按照图中的步骤说明,创建或者迁移命名空间

image.png

1.3. 校验命名空间(迁移已有的命名空间不需要该步骤)

image.png image.png

注意:如果你的命名空间是一个域名,则需要做「DNS TXT Record」DNS文本记录校验

image.png

1.4. 生成用户访问令牌

还是右上角,找到「View Account」,点击之后会进到一个很简洁的账号管理界面,需要在这个页面生成后续流程所需的用户访问令牌

image.png image.png

保存好你的令牌,不要泄露出去,如果重新生成了令牌,原有的令牌过一段时间之后将会失效。

image.png

1.5. 配置用户访问令牌到Maven环境

将用户访问令牌配置到Maven的settings.xml配置文件中。此处以IDEA为例,请按照你实际具体使用的Maven环境进行配置。

image.png image.png

2. GPG密钥工具准备

2.1 GPG密钥工具下载安装

打开 GunPG Download ,找到 「GnuPG binary releases」项,根据电脑的环境下载安装合适的GPG工具。下面以Windwos 10/11为例。

image.png image.png

Tips:开源不易,有能力可以适当支持一下GPG的开源工作

image.png

软件支持中文,安装目录随意,步骤选项默认即可。

2.2 密钥生成和上传公钥服务器

GPG4Win支持GUI图形界面和命令行生成密钥

Maven中央服务器支持的 GPG 密钥服务器有好几个,如遇报错或者网络问题,可以切换尝试以下服务器:

keyserver.ubuntu.com

keys.openpgp.org

pgp.mit.edu

2.2.1 图形界面生成密钥

打开Kleopatra密钥管理工具(即GPG的GUI客户端)

image.png image.png image.png image.png image.png
2.2.2 命令行生成密钥

打开终端工具输入gpg --version 如果没有输出版本信息说明环境不对,检查一下安装。

下图中用到的命令

输出GPG版本环境信息:gpg --version

生成密钥:gpg --gen-key image.png 下图中用到的命令

查看秘钥:gpg --list-keys

上传密钥到公钥服务器:gpg --keyserver keyserver.ubuntu.com --send-keys 密钥pub指纹

验证是否成功上传到公钥服务器:gpg --keyserver keyserver.ubuntu.com --recv-keys 密钥pub指纹 image.png

2.3 配置GPG密钥到Maven环境

将GPG密钥配置到Maven的settings.xml配置文件中。具体配置文件可以查看上文中的 1.5. 配置用户访问令牌到Maven环境步骤

image.png

3. 部署流程

  1. 部署不成功的,可以参考一下下面的配置。

  2. 在阅读下面的配置之前,需要注意的如下几点:

  • 项目的groupId必须和Maven Central的命名空间一致

  • pom.xml 中必须包括:namedescriptionurllicensesdevelopersscm 等基本信息标签,在执行 deploy 的时候,需要使用 Maven 配置环境中的 GPG 密钥 profile (即上面我们配的gpg密钥)对源码包、文档包、字节码包进行 GPG 数字签名。

  • 多模块项目只需要在父工程的 pom.xml 中声明这些,子模块 pom.xml 中只需要修改相应的一些信息即可,如 name 标签。

  • central-publishing-maven-plugin 插件中的publishingServerId需要与Maven配置环境文件settings.xml中的serverid保持一致(即步骤1.5. 配置用户访问令牌到Maven环境中我们配置的server

3.1 项目pom.xml配置

以下pom.xml,有五个插件是必须的,分别是:

  1. maven-compiler-plugin插件用于编译字节码包
  2. maven-source-plugin插件用于构建源码包
  3. maven-javadoc-plugin插件用于构建文档包
  4. maven-gpg-plugin插件用于打包GPG数字签名
  5. central-publishing-maven-plugin插件用于推送包到Maven Central

下面以我成功发布的项目单模块项目 netty-socketio-spring-boot-starter 为例,多模块可参考我另一个成功发布的项目 mapstruct-convert

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!-- parent表示依赖父工程,这个不是必须的,如果你的项目没有依赖任何父工程,这个可以删掉 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!-- 下面的一些基本的标签,必须要有 -->
    <groupId>io.github.deersunny</groupId>
    <artifactId>netty-socketio-spring-boot-starter</artifactId>
    <version>2.0.0</version>
    <name>netty-socketio-spring-boot-starter</name>
    <description>The project is NettySocketIO Spring Boot Starter</description>
    <url>https://github.com/ColorDreams/${project.artifactId}</url>
    <packaging>jar</packaging>
    
    <!-- 协议信息标签,必须要有 -->
    <licenses>
        <license>
            <name>Apache License, Version 2.0</name>
            <url>https://www.apache.org/licenses/LICENSE-2.0</url>
            <distribution>repo</distribution>
        </license>
    </licenses>
    
    <!-- 开发人员信息,必须要有,改成你自己的,developer 可以配置多个 -->
    <developers>
        <developer>
            <name>ColorDreams</name>
            <email>505073804@qq.com</email>
            <timezone>+8</timezone>
        </developer>
    </developers>
    
    <!-- 仓库信息,必须要有,改成你自己的 -->
    <scm>
        <url>https://github.com/ColorDreams/${project.artifactId}</url>
    </scm>
    <!-- issue管理配置,改成你自己的 -->
    <!-- 可以使用 <issueManagement/> 闭合标签,表示忽略该项配置,该标签必须要有,不能删除 -->
    <issueManagement>
        <system>GitHub</system>
        <url>https://github.com/ColorDreams/${project.artifactId}/issues</url>
    </issueManagement>
    
    <properties>
        <!-- project java version -->
        <java.version>17</java.version>
        <!-- project encoding -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <!-- project dependency package version -->
        <spring-boot.version>3.4.5</spring-boot.version>
        <netty-socketio.version>2.0.13</netty-socketio.version>

        <!-- project dependency plugin version -->
        <apache-maven-plugins.groupId>org.apache.maven.plugins</apache-maven-plugins.groupId>
        <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
        <maven-source-plugin.version>3.3.1</maven-source-plugin.version>
        <maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version>
        <maven-gpg-plugin.version>3.2.7</maven-gpg-plugin.version>
        <license-maven-plugin.version>5.0.0</license-maven-plugin.version>
        <central-publishing-maven-plugin.version>0.7.0</central-publishing-maven-plugin.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- spring-hooks-dependencies dependency -->
            <!-- https://central.sonatype.com/artifact/org.springframework.boot/spring-boot-dependencies -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- netty-socketio dependency -->
            <!-- https://central.sonatype.com/artifact/com.corundumstudio.socketio/netty-socketio -->
            <dependency>
                <groupId>com.corundumstudio.socketio</groupId>
                <artifactId>netty-socketio</artifactId>
                <version>${netty-socketio.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}-${project.version}</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>

        <plugins>
            <!-- 编译插件,插件是必须的 -->
            <!-- compiler plugin -->
            <!-- https://central.sonatype.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
            <plugin>
                <groupId>${apache-maven-plugins.groupId}</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                            <version>${spring-boot.version}</version>
                        </path>
                    </annotationProcessorPaths>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>

            <!-- jar源码包生成插件,插件是必须的 -->
            <!-- build source package plugin -->
            <!-- https://central.sonatype.com/artifact/org.apache.maven.plugins/maven-source-plugin -->
            <plugin>
                <groupId>${apache-maven-plugins.groupId}</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>${maven-source-plugin.version}</version>
                <configuration>
                    <!-- 源码包随着项目打成的jar包安装到本地仓库或者私服、公服 -->
                    <attach>true</attach>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>jar-no-fork</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <!-- javadoc生成插件,插件是必须的 -->
            <!-- gen javadoc plugin -->
            <!-- https://central.sonatype.com/artifact/org.apache.maven.plugins/maven-javadoc-plugin -->
            <plugin>
                <groupId>${apache-maven-plugins.groupId}</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>${maven-javadoc-plugin.version}</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- 禁用严格语法检测 -->
                    <doclint>none</doclint>
                </configuration>
            </plugin>

            <!-- GPG签名插件,插件是必须的 -->
            <!-- gpg sign plugin -->
            <!-- https://central.sonatype.com/artifact/org.apache.maven.plugins/maven-gpg-plugin -->
            <plugin>
                <groupId>${apache-maven-plugins.groupId}</groupId>
                <artifactId>maven-gpg-plugin</artifactId>
                <version>${maven-gpg-plugin.version}</version>
                <configuration>
                    <gpgArguments>
                        <arg>--pinentry-mode</arg>
                        <arg>loopback</arg>
                    </gpgArguments>
                </configuration>
                <executions>
                    <execution>
                        <id>sign-artifacts</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>sign</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <!-- 发布到maven仓库中心插件,插件是必须的 -->
            <!-- publishing to central server plugin -->
            <!-- https://central.sonatype.com/artifact/org.sonatype.central/central-publishing-maven-plugin -->
            <plugin>
                <groupId>org.sonatype.central</groupId>
                <artifactId>central-publishing-maven-plugin</artifactId>
                <version>${central-publishing-maven-plugin.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <!-- 注意:这类必须要和Maven配置环境中的server id对应上 -->
                    <publishingServerId>central</publishingServerId>
                </configuration>
            </plugin>

            <!-- 开源协议管理插件,用于给源码生成开源协议信息头,插件不是必须的,可以删掉 -->
            <!-- gen code license plugin -->
            <!-- https://central.sonatype.com/artifact/com.mycila/license-maven-plugin -->
            <plugin>
                <groupId>com.mycila</groupId>
                <artifactId>license-maven-plugin</artifactId>
                <version>${license-maven-plugin.version}</version>
                <configuration>
                    <basedir>${basedir}</basedir>
                    <quiet>false</quiet>
                    <failIfMissing>true</failIfMissing>
                    <aggregate>false</aggregate>
                    <useDefaultExcludes>true</useDefaultExcludes>
                    <useDefaultMapping>true</useDefaultMapping>
                    <mapping>
                        <java>SLASHSTAR_STYLE</java>
                    </mapping>
                    <strictCheck>true</strictCheck>
                    <failIfMissing>true</failIfMissing>
                    <aggregate>true</aggregate>
                    <strictCheck>true</strictCheck>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <licenseSets>
                        <licenseSet>
                            <header>${basedir}/LICENSE-2.0.txt</header>
<!--                            <header>com/mycila/maven/plugin/license/templates/APACHE-2.txt</header>-->
                            <excludes>
                                <exclude>**/README</exclude>
                                <exclude>src/test/resources/**</exclude>
                                <exclude>src/main/resources/**</exclude>
                                <exclude>**/generated/**</exclude>
                                <exclude>target/**</exclude>
                            </excludes>
                            <includes>
                                <include>src/main/java/**/*.java</include>
                            </includes>
                        </licenseSet>
                    </licenseSets>
                    <properties>
                        <owner>秋辞未寒</owner>
                        <email>545073804@qq.com</email>
                        <year>2025</year>
                    </properties>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    
    <!-- 依赖下载加速镜像,可以不用配 -->
    <repositories>
        <repository>
            <id>public</id>
            <name>huawei nexus</name>
            <url>https://mirrors.huaweicloud.com/repository/maven/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>

    <!-- 插件依赖下载加速镜像,可以不用配 -->
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>huawei nexus</name>
            <url>https://mirrors.huaweicloud.com/repository/maven/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
    
    <!-- 部署管理配置,基本不需要修改,原来是必须要配的 -->
    <!-- 新的发布方式已经不需要该配置了,可以删掉 -->
    <distributionManagement>
        <snapshotRepository>
            <id>ossrh</id>
            <name>OSS Snapshots Repositories</name>
            <url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
        </snapshotRepository>
        <repository>
            <id>ossrh</id>
            <name>OSS Staging Repositories</name>
            <url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
        </repository>
    </distributionManagement>

</project>

3.2 部署

使用 lifecycle 中的 deploy 指令即可实现一键打包部署

image.png

3.3 注意事项

使用IDEA打包时,需要使用 lifecycle 中的命令项,禁止使用 plugins 中的插件

image.png

发布快照包需要到Maven Central命名空间管理界面,启用 Snapshots 模式

image.png