发布Artifact


当我们使用commons-logging这些第三方开源库的时候,我们实际上是通过Maven自动下载它的jar包,并根据其pom.xml解析依赖,自动把相关依赖包都下载后加入到classpath。

那么问题来了:当我们自己写了一个牛逼的开源库时,非常希望别人也能使用,总不能直接放个jar包的链接让别人下载吧?

如果我们把自己的开源库放到Maven的repo中,那么,别人只需按标准引用groupId:artifactId:version,即可自动下载jar包以及相关依赖。因此,本节我们介绍如何发布一个库到Maven的repo中。

把自己的库发布到Maven的repo中有好几种方法,我们介绍3种最常用的方法。

以静态文件发布

如果我们观察一个中央仓库的Artifact结构,例如Commons Math,它的groupId是org.apache.commons,artifactId是commons-math3,以版本3.6.1为例,发布在中央仓库的文件夹路径就是https://repo1.maven.org/maven2/org/apache/commons/commons-math3/3.6.1/,在此文件夹下,commons-math3-3.6.1.jar就是发布的jar包,commons-math3-3.6.1.pom就是它的pom.xml描述文件,commons-math3-3.6.1-sources.jar是源代码,commons-math3-3.6.1-javadoc.jar是文档。其它以.asc.md5.sha1结尾的文件分别是GPG签名、MD5摘要和SHA-1摘要。

我们只要按照这种目录结构组织文件,它就是一个有效的Maven仓库。

我们以广受好评的开源项目how-to-become-rich为例,先创建Maven工程目录结构如下:

  1. how-to-become-rich
  2. ├── maven-repo <-- Maven本地文件仓库
  3. ├── pom.xml <-- 项目文件
  4. ├── src
  5. ├── main
  6. ├── java <-- 源码目录
  7. └── resources <-- 资源目录
  8. └── test
  9. ├── java <-- 测试源码目录
  10. └── resources <-- 测试资源目录
  11. └── target <-- 编译输出目录

pom.xml中添加如下内容:

  1. <project ...>
  2. ...
  3. <distributionManagement>
  4. <repository>
  5. <id>local-repo-release</id>
  6. <name>GitHub Release</name>
  7. <url>file://${project.basedir}/maven-repo</url>
  8. </repository>
  9. </distributionManagement>
  10. <build>
  11. <plugins>
  12. <plugin>
  13. <artifactId>maven-source-plugin</artifactId>
  14. <executions>
  15. <execution>
  16. <id>attach-sources</id>
  17. <phase>package</phase>
  18. <goals>
  19. <goal>jar-no-fork</goal>
  20. </goals>
  21. </execution>
  22. </executions>
  23. </plugin>
  24. <plugin>
  25. <artifactId>maven-javadoc-plugin</artifactId>
  26. <executions>
  27. <execution>
  28. <id>attach-javadocs</id>
  29. <phase>package</phase>
  30. <goals>
  31. <goal>jar</goal>
  32. </goals>
  33. </execution>
  34. </executions>
  35. </plugin>
  36. </plugins>
  37. </build>
  38. </project>

注意到<distributionManagement>,它指示了发布的软件包的位置,这里的<url>是项目根目录下的maven-repo目录,在<build>中定义的两个插件maven-source-pluginmaven-javadoc-plugin分别用来创建源码和javadoc,如果不想发布源码,可以把对应的插件去掉。

我们直接在项目根目录下运行Maven命令mvn clean package deploy,如果一切顺利,我们就可以在maven-repo目录下找到部署后的所有文件如下:

GitHubmichaelliaohow-to-become-rich/maven-repo

▸ com/itranswarp/rich/how-to-become-rich)

▸ 1.0.0)

▤ how-to-become-rich-1.0.0-javadoc.jar)

▤ how-to-become-rich-1.0.0-javadoc.jar.md5)

▤ how-to-become-rich-1.0.0-javadoc.jar.sha1)

▤ how-to-become-rich-1.0.0-sources.jar)

▤ how-to-become-rich-1.0.0-sources.jar.md5)

▤ how-to-become-rich-1.0.0-sources.jar.sha1)

▤ how-to-become-rich-1.0.0.jar)

▤ how-to-become-rich-1.0.0.jar.md5)

▤ how-to-become-rich-1.0.0.jar.sha1)

▤ how-to-become-rich-1.0.0.pom)

▤ how-to-become-rich-1.0.0.pom.md5)

▤ how-to-become-rich-1.0.0.pom.sha1)

▤ maven-metadata.xml)

▤ maven-metadata.xml.md5)

▤ maven-metadata.xml.sha1)

最后一步,是把这个工程推到GitHub上,并选择Settings-GitHub Pages,选择master branch启用Pages服务:

enable-github-pages

这样,把全部内容推送至GitHub后,即可作为静态网站访问Maven的repo,它的地址是https://michaelliao.github.io/how-to-become-rich/maven-repo/。版本1.0.0对应的jar包地址是:

  1. https://michaelliao.github.io/how-to-become-rich/maven-repo/com/itranswarp/rich/how-to-become-rich/1.0.0/how-to-become-rich-1.0.0.jar

现在,如果其他人希望引用这个Maven包,我们可以告知如下依赖即可:

  1. <dependency>
  2. <groupId>com.itranswarp.rich</groupId>
  3. <artifactId>how-to-become-rich</artifactId>
  4. <version>1.0.0</version>
  5. </dependency>

但是,除了正常导入依赖外,对方还需要再添加一个<repository>的声明,即使用方完整的pom.xml如下:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>example</groupId>
  6. <artifactId>how-to-become-rich-usage</artifactId>
  7. <version>1.0-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <properties>
  10. <maven.compiler.source>11</maven.compiler.source>
  11. <maven.compiler.target>11</maven.compiler.target>
  12. <java.version>11</java.version>
  13. </properties>
  14. <repositories>
  15. <repository>
  16. <id>github-rich-repo</id>
  17. <name>The Maven Repository on Github</name>
  18. <url>https://michaelliao.github.io/how-to-become-rich/maven-repo/</url>
  19. </repository>
  20. </repositories>
  21. <dependencies>
  22. <dependency>
  23. <groupId>com.itranswarp.rich</groupId>
  24. <artifactId>how-to-become-rich</artifactId>
  25. <version>1.0.0</version>
  26. </dependency>
  27. </dependencies>
  28. </project>

<repository>中,我们必须声明发布的Maven的repo地址,其中<id><name>可以任意填写,<url>填入GitHub Pages提供的地址+/maven-repo/后缀。现在,即可正常引用这个库并编写代码如下:

  1. Millionaire millionaire = new Millionaire();
  2. System.out.println(millionaire.howToBecomeRich());

有的童鞋会问,为什么使用commons-logging等第三方库时,并不需要声明repo地址?这是因为这些库都是发布到Maven中央仓库的,发布到中央仓库后,不需要告诉Maven仓库地址,因为它知道中央仓库的地址默认是https://repo1.maven.org/maven2/,也可以通过~/.m2/settings.xml指定一个代理仓库地址以替代中央仓库来提高速度(参考依赖管理的Maven镜像)。

因为GitHub Pages并不会把我们发布的Maven包同步到中央仓库,所以自然使用方必须手动添加一个我们提供的仓库地址。

此外,通过GitHub Pages发布Maven repo时需要注意一点,即不要改动已发布的版本。因为Maven的仓库是不允许修改任何版本的,对一个库进行修改的唯一方法是发布一个新版本。但是通过静态文件的方式发布repo,实际上我们是可以修改jar文件的,但最好遵守规范,不要修改已发布版本。

通过Nexus发布到中央仓库

有的童鞋会问,能不能把自己的开源库发布到Maven的中央仓库,这样用户就不需要声明repo地址,可以直接引用,显得更专业。

当然可以,但我们不能直接发布到Maven中央仓库,而是通过曲线救国的方式,发布到central.sonatype.org,它会定期自动同步到Maven的中央仓库。Nexus是一个支持Maven仓库的软件,由Sonatype开发,有免费版和专业版两个版本,很多大公司内部都使用Nexus作为自己的私有Maven仓库,而这个central.sonatype.org相当于面向开源的一个Nexus公共服务。

所以,第一步是在central.sonatype.org上注册一个账号,注册链接非常隐蔽,可以自己先找找,找半小时没找到点这里)查看攻略。

如果注册顺利并审核通过,会得到一个登录账号,然后,通过这个页面一步一步操作就可以成功地将自己的Artifact发布到Nexus上,再耐心等待几个小时后,你的Artifact就会出现在Maven的中央仓库中。

这里简单提一下发布重点与难点:

  • 必须正确创建GPG签名,Linux和Mac下推荐使用gnupg2;
  • 必须在~/.m2/settings.xml中配置好登录用户名和口令,以及GPG口令:
  1. <settings ...>
  2. ...
  3. <servers>
  4. <server>
  5. <id>ossrh</id>
  6. <username>OSSRH-USERNAME</username>
  7. <password>OSSRH-PASSWORD</password>
  8. </server>
  9. </servers>
  10. <profiles>
  11. <profile>
  12. <id>ossrh</id>
  13. <activation>
  14. <activeByDefault>true</activeByDefault>
  15. </activation>
  16. <properties>
  17. <gpg.executable>gpg2</gpg.executable>
  18. <gpg.passphrase>GPG-PASSWORD</gpg.passphrase>
  19. </properties>
  20. </profile>
  21. </profiles>
  22. </settings>

在待发布的Artifact的pom.xml中添加OSS的Maven repo地址,以及maven-jar-pluginmaven-source-pluginmaven-javadoc-pluginmaven-gpg-pluginnexus-staging-maven-plugin

  1. <project ...>
  2. ...
  3. <distributionManagement>
  4. <snapshotRepository>
  5. <id>ossrh</id>
  6. <url>https://oss.sonatype.org/content/repositories/snapshots</url>
  7. </snapshotRepository>
  8. <repository>
  9. <id>ossrh</id>
  10. <name>Nexus Release Repository</name>
  11. <url>http://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
  12. </repository>
  13. </distributionManagement>
  14. <build>
  15. <plugins>
  16. <plugin>
  17. <groupId>org.apache.maven.plugins</groupId>
  18. <artifactId>maven-jar-plugin</artifactId>
  19. <executions>
  20. <execution>
  21. <goals>
  22. <goal>jar</goal>
  23. <goal>test-jar</goal>
  24. </goals>
  25. </execution>
  26. </executions>
  27. </plugin>
  28. <plugin>
  29. <groupId>org.apache.maven.plugins</groupId>
  30. <artifactId>maven-source-plugin</artifactId>
  31. <executions>
  32. <execution>
  33. <id>attach-sources</id>
  34. <goals>
  35. <goal>jar-no-fork</goal>
  36. </goals>
  37. </execution>
  38. </executions>
  39. </plugin>
  40. <plugin>
  41. <groupId>org.apache.maven.plugins</groupId>
  42. <artifactId>maven-javadoc-plugin</artifactId>
  43. <executions>
  44. <execution>
  45. <id>attach-javadocs</id>
  46. <goals>
  47. <goal>jar</goal>
  48. </goals>
  49. <configuration>
  50. <additionalOption>
  51. <additionalOption>-Xdoclint:none</additionalOption>
  52. </additionalOption>
  53. </configuration>
  54. </execution>
  55. </executions>
  56. </plugin>
  57. <plugin>
  58. <groupId>org.apache.maven.plugins</groupId>
  59. <artifactId>maven-gpg-plugin</artifactId>
  60. <executions>
  61. <execution>
  62. <id>sign-artifacts</id>
  63. <phase>verify</phase>
  64. <goals>
  65. <goal>sign</goal>
  66. </goals>
  67. </execution>
  68. </executions>
  69. </plugin>
  70. <plugin>
  71. <groupId>org.sonatype.plugins</groupId>
  72. <artifactId>nexus-staging-maven-plugin</artifactId>
  73. <version>1.6.3</version>
  74. <extensions>true</extensions>
  75. <configuration>
  76. <serverId>ossrh</serverId>
  77. <nexusUrl>https://oss.sonatype.org/</nexusUrl>
  78. <autoReleaseAfterClose>true</autoReleaseAfterClose>
  79. </configuration>
  80. </plugin>
  81. </plugins>
  82. </build>
  83. </project>

最后执行命令mvn clean package deploy即可发布至central.sonatype.org

此方法前期需要复杂的申请账号和项目的流程,后期需要安装调试GPG,但只要跑通流程,后续发布都只需要一行命令。

发布到私有仓库

通过nexus-staging-maven-plugin除了可以发布到central.sonatype.org外,也可以发布到私有仓库,例如,公司内部自己搭建的Nexus服务器。

如果没有私有Nexus服务器,还可以发布到GitHub Packages。GitHub Packages是GitHub提供的仓库服务,支持Maven、NPM、Docker等。使用GitHub Packages时,无论是发布Artifact,还是引用已发布的Artifact,都需要明确的授权Token,因此,GitHub Packages只能作为私有仓库使用。

在发布前,我们必须首先登录后在用户的Settings-Developer settings-Personal access tokens中创建两个Token,一个用于发布,一个用于使用。发布Artifact的Token必须有repowrite:packagesread:packages权限:

token-scopes

使用Artifact的Token只需要read:packages权限。

在发布端,把GitHub的用户名和发布Token写入~/.m2/settings.xml配置中:

  1. <settings ...>
  2. ...
  3. <servers>
  4. <server>
  5. <id>github-release</id>
  6. <username>GITHUB-USERNAME</username>
  7. <password>f052...c21f</password>
  8. </server>
  9. </servers>
  10. </settings>

然后,在需要发布的Artifact的pom.xml中,添加一个<repository>声明:

  1. <project ...>
  2. ...
  3. <distributionManagement>
  4. <repository>
  5. <id>github-release</id>
  6. <name>GitHub Release</name>
  7. <url>https://maven.pkg.github.com/michaelliao/complex</url>
  8. </repository>
  9. </distributionManagement>
  10. </project>

注意到<id>~/.m2/settings.xml配置中的<id>要保持一致,因为发布时Maven根据id找到用于登录的用户名和Token,才能成功上传文件到GitHub。我们直接通过命令mvn clean package deploy部署,成功后,在GitHub用户页面可以看到该Artifact:

github-packages

完整的配置请参考complex项目,这是一个非常简单的支持复数运算的库。

使用该Artifact时,因为GitHub的Package只能作为私有仓库使用,所以除了在使用方的pom.xml中声明<repository>外:

  1. <project ...>
  2. ...
  3. <repositories>
  4. <repository>
  5. <id>github-release</id>
  6. <name>GitHub Release</name>
  7. <url>https://maven.pkg.github.com/michaelliao/complex</url>
  8. </repository>
  9. </repositories>
  10. <dependencies>
  11. <dependency>
  12. <groupId>com.itranswarp</groupId>
  13. <artifactId>complex</artifactId>
  14. <version>1.0.0</version>
  15. </dependency>
  16. </dependencies>
  17. ...
  18. </project>

还需要把有读权限的Token配置到~/.m2/settings.xml文件中。

练习

使用maven-deploy-plugin把Artifact发布到本地。

小结

使用Maven发布一个Artifact时:

  • 可以发布到本地,然后推送到远程Git库,由静态服务器提供基于网页的repo服务,使用方必须声明repo地址;
  • 可以发布到central.sonatype.org,并自动同步到Maven中央仓库,需要前期申请账号以及本地配置;
  • 可以发布到GitHub Packages作为私有仓库使用,必须提供Token以及正确的权限才能发布和使用。

读后有收获可以支付宝请作者喝咖啡:

发布Artifact - 图4