1. 概述
在 DevOps 流行的今天,一天内多次构建和部署应用已是常态。✅
为了区分不同构建产物,每次构建都会被打上唯一的版本号。而有时我们还需要在程序中对这些版本号字符串进行比较,比如判断是否需要升级、做灰度发布控制等。
本文将介绍几种在 Java 中比较版本号的常用方式,涵盖多个流行库的实践方案。最后还会手撸一个通用的自定义实现,方便你在标准库不满足需求时快速扩展。
⚠️ 注意:本文面向有经验的开发者,基础概念不再赘述。
2. 使用 maven-artifact
Maven 作为老牌构建工具,其版本管理能力非常成熟。我们可以直接借用 maven-artifact
模块中的版本比较逻辑。
2.1 Maven 依赖
引入最新版依赖(截至写作时为 3.6.3):
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>3.6.3</version>
</dependency>
2.2 ComparableVersion 类
核心类是 ComparableVersion
,它支持无限层级的版本组件比较,且实现了 Comparable
接口,可以直接用于排序。
示例代码如下:
ComparableVersion version1_1 = new ComparableVersion("1.1");
ComparableVersion version1_2 = new ComparableVersion("1.2");
ComparableVersion version1_3 = new ComparableVersion("1.3");
assertTrue(version1_1.compareTo(version1_2) < 0);
assertTrue(version1_3.compareTo(version1_2) > 0);
相同版本返回 0:
ComparableVersion version1_1_0 = new ComparableVersion("1.1.0");
assertEquals(0, version1_1.compareTo(version1_1_0));
✅ 优势:自动忽略末尾的 .0
,语义化处理更贴近实际使用场景。
2.3 分隔符与修饰符(Qualifiers)
ComparableVersion
支持 .
和 -
作为分隔符:
.
用于划分主次版本-
后接修饰符(qualifier),表示预发布版本
支持的常见 qualifier(按优先级升序排列):
alpha
<beta
<milestone
<rc
<snapshot
< 正式版
示例验证:
ComparableVersion version1_1_alpha = new ComparableVersion("1.1-alpha");
assertTrue(version1_1.compareTo(version1_1_alpha) > 0); // 1.1 > 1.1-alpha
多个 qualifier 的比较:
ComparableVersion version1_1_beta = new ComparableVersion("1.1-beta");
ComparableVersion version1_1_milestone = new ComparableVersion("1.1-milestone");
ComparableVersion version1_1_rc = new ComparableVersion("1.1-rc");
ComparableVersion version1_1_snapshot = new ComparableVersion("1.1-snapshot");
assertTrue(version1_1_alpha.compareTo(version1_1_beta) < 0);
assertTrue(version1_1_beta.compareTo(version1_1_milestone) < 0);
assertTrue(version1_1_rc.compareTo(version1_1_snapshot) < 0);
assertTrue(version1_1_snapshot.compareTo(version1_1) < 0);
✅ 冷知识:你甚至可以自定义 qualifier,比如 -exp
、-test
,它们会按字典序排在已知 qualifier 之后,且不区分大小写。
ComparableVersion version1_1_c = new ComparableVersion("1.1-c");
ComparableVersion version1_1_z = new ComparableVersion("1.1-z");
ComparableVersion version1_1_1 = new ComparableVersion("1.1.1");
assertTrue(version1_1_c.compareTo(version1_1_z) < 0);
assertTrue(version1_1_z.compareTo(version1_1_1) < 0); // 自定义 qualifier < 数字版本
3. 使用 gradle-core
Gradle 也有自己的版本比较机制,位于 gradle-core
模块中。
3.1 Maven 依赖
<dependency>
<groupId>org.gradle</groupId>
<artifactId>gradle-core</artifactId>
<version>6.1.1</version>
</dependency>
⚠️ 注意:该包体积较大,仅为了版本比较而引入可能得不偿失,建议仅在已有 Gradle 集成的项目中使用。
3.2 VersionNumber 类
核心类是 VersionNumber
,用法类似 Maven 的 ComparableVersion
:
VersionNumber version1_1 = VersionNumber.parse("1.1");
VersionNumber version1_2 = VersionNumber.parse("1.2");
VersionNumber version1_3 = VersionNumber.parse("1.3");
assertTrue(version1_1.compareTo(version1_2) < 0);
assertTrue(version1_3.compareTo(version1_2) > 0);
VersionNumber version1_1_0 = VersionNumber.parse("1.1.0");
assertEquals(0, version1_1.compareTo(version1_1_0));
3.3 版本组件限制
VersionNumber
只支持最多五层结构:
- Major
- Minor
- Micro
- Patch
- Qualifier
示例:
VersionNumber version1_1_1_1_alpha = VersionNumber.parse("1.1.1.1-alpha");
assertTrue(version1_1.compareTo(version1_1_1_1_alpha) < 0);
VersionNumber version1_1_beta = VersionNumber.parse("1.1.0.0-beta");
assertTrue(version1_1_beta.compareTo(version1_1_1_1_alpha) < 0);
3.4 支持的版本格式
支持两种常见格式:
Major.Minor.Micro-Qualifier
Major.Minor.Micro.Patch-Qualifier
VersionNumber version1_1_1_snapshot = VersionNumber.parse("1.1.1-snapshot");
assertTrue(version1_1_1_1_alpha.compareTo(version1_1_1_snapshot) < 0);
❌ 局限:灵活性不如 Maven,不支持任意深度的版本号。
4. 使用 jackson-core
Jackson 不只是 JSON 处理工具,它的 jackson-core
也提供了版本管理功能。
4.1 Maven 依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.16.0</version>
</dependency>
4.2 Version 类
Version
类不仅能存版本号,还能带上 groupId
和 artifactId
,适合做组件版本管理。
构造函数签名:
public Version(int major, int minor, int patchLevel, String snapshotInfo, String groupId, String artifactId)
使用示例:
Version version1_1 = new Version(1, 1, 0, null, null, null);
Version version1_2 = new Version(1, 2, 0, null, null, null);
Version version1_3 = new Version(1, 3, 0, null, null, null);
assertTrue(version1_1.compareTo(version1_2) < 0);
assertTrue(version1_3.compareTo(version1_2) > 0);
Version version1_1_1 = new Version(1, 1, 1, null, null, null);
assertTrue(version1_1.compareTo(version1_1_1) < 0);
4.3 snapshotInfo 字段
用于标识快照版本:
Version version1_1_snapshot = new Version(1, 1, 0, "snapshot", null, null);
assertEquals(1, version1_1.compareTo(version1_1_snapshot)); // 正式版 > 快照版
判断是否为快照:
assertTrue(version1_1_snapshot.isSnapshot());
4.4 groupId 与 artifactId 比较
比较时还会按字典序比较 groupId
和 artifactId
:
Version version1_1_maven = new Version(1, 1, 0, null, "org.apache.maven", null);
Version version1_1_gradle = new Version(1, 1, 0, null, "org.gradle", null);
assertTrue(version1_1_maven.compareTo(version1_1_gradle) < 0);
✅ 适用场景:适合在插件系统或模块化架构中做精确版本匹配。
5. 使用 Semver4J
如果你遵循 语义化版本规范(SemVer),那 semver4j
是个不错的选择。
5.1 Maven 依赖
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>semver4j</artifactId>
<version>3.1.0</version>
</dependency>
5.2 Semver 类
创建版本对象:
Semver version1_1 = new Semver("1.1.0");
Semver version1_2 = new Semver("1.2.0");
Semver version1_3 = new Semver("1.3.0");
assertTrue(version1_1.compareTo(version1_2) < 0);
assertTrue(version1_3.compareTo(version1_2) > 0);
内部会解析出 major
、minor
、patch
三个部分。
5.3 内置比较方法
提供语义清晰的判断方法:
Semver version1_1_alpha = new Semver("1.1.0-alpha");
assertTrue(version1_1.isGreaterThan(version1_1_alpha));
Semver version1_1_beta = new Semver("1.1.0-beta");
assertTrue(version1_1_alpha.isLowerThan(version1_1_beta));
assertTrue(version1_1.isEqualTo("1.1.0"));
还能获取两个版本的主要差异层级:
assertEquals(VersionDiff.MAJOR, version1_1.diff("2.1.0"));
assertEquals(VersionDiff.MINOR, version1_1.diff("1.2.3"));
assertEquals(VersionDiff.PATCH, version1_1.diff("1.1.1"));
5.4 稳定性判断
通过 isStable()
判断是否为稳定版:
assertTrue(version1_1.isStable());
assertFalse(version1_1_alpha.isStable());
✅ 优点:API 设计直观,符合 SemVer 规范,适合标准化项目。
6. 自定义解决方案
如果上述库都不满足你的特殊需求(比如自定义分隔符、特殊修饰符),那就得自己写了。
下面是一个简单粗暴但实用的实现,适用于纯数字点分版本:
public static int compareVersions(String version1, String version2) {
int comparisonResult = 0;
String[] version1Splits = version1.split("\\.");
String[] version2Splits = version2.split("\\.");
int maxLengthOfVersionSplits = Math.max(version1Splits.length, version2Splits.length);
for (int i = 0; i < maxLengthOfVersionSplits; i++){
Integer v1 = i < version1Splits.length ? Integer.parseInt(version1Splits[i]) : 0;
Integer v2 = i < version2Splits.length ? Integer.parseInt(version2Splits[i]) : 0;
int compare = v1.compareTo(v2);
if (compare != 0) {
comparisonResult = compare;
break;
}
}
return comparisonResult;
}
测试用例:
assertTrue(VersionCompare.compareVersions("1.0.1", "1.1.2") < 0);
assertTrue(VersionCompare.compareVersions("1.0.1", "1.10") < 0);
assertTrue(VersionCompare.compareVersions("1.1.2", "1.0.1") > 0);
assertTrue(VersionCompare.compareVersions("1.1.2", "1.2.0") < 0);
assertEquals(0, VersionCompare.compareVersions("1.3.0", "1.3"));
⚠️ 局限性:
- 仅支持纯数字
- 不支持
-alpha
这类修饰符
扩展思路
若需支持字母,可用正则拆分:
// 示例:拆分 "1.2.3-beta" -> ["1","2","3","beta"]
String[] parts = version.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)|\\.");
然后对每段做类型判断:数字转 Integer
,字符串按字典序比较。
7. 总结
方案 | 适用场景 | 推荐指数 |
---|---|---|
maven-artifact |
通用性强,支持复杂 qualifier | ⭐⭐⭐⭐⭐ |
gradle-core |
Gradle 生态内使用 | ⭐⭐⭐ |
jackson-core |
组件级版本管理 | ⭐⭐⭐⭐ |
semver4j |
严格遵循 SemVer | ⭐⭐⭐⭐⭐ |
自定义 | 特殊格式或轻量需求 | ⭐⭐⭐⭐ |
✅ 建议:
- 大多数情况优先选
maven-artifact
或semver4j
- 已有依赖的项目可复用对应库
- 性能敏感场景可考虑轻量自定义实现
所有示例代码已上传至 GitHub:https://github.com/yourname/java-version-compare-demo