Ubuntu 22.04 安装 Apache Commons Lang 指南
在 Ubuntu 22.04 上从零引入 Apache Commons Lang(commons-lang3)3.21.0,覆盖版本选型与 2.6 断代对照、Maven 在线依赖与官方二进制包两套方案、多版本管理,附真实回显与排障。
Ubuntu 22.04 安装 Apache Commons Lang 指南
目标:在 Ubuntu 22.04 上从零搭好 Apache Commons Lang(commons-lang3)3.21.0 的使用环境,完成 JDK 与 Maven 准备、Maven 在线依赖与官方二进制包两种获取方式、升级/降级与多版本管理,并跑通真实工具类调用验证。以下全部命令与回显均已在 Ubuntu 22.04(x86_64)实测通过。
一、环境信息
本节先固定本次实测所用软硬件基线,便于读者逐项对照复现。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- JDK:OpenJDK 17.0.20.1(
java -version实测) - 构建工具:Apache Maven 3.6.3(系统包仓库默认版本)
- 下载工具:curl 7.81.0(系统自带)
- 目标库:Apache Commons Lang 3.21.0(坐标
org.apache.commons:commons-lang3,2026-09-29 发布) - 传递依赖:无(零传递依赖,实测
mvn dependency:tree确认,见 4.7) - Maven 示例工程目录:
/opt/lang-demo - 官方二进制包工作目录:
/opt/lang-jars
先确认系统版本与架构:
cat /etc/os-release | head -3
uname -m
输出:
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
x86_64
再确认 JDK 与 Maven 版本基线,后续所有编译运行都基于它:
java -version
mvn -v | head -3
输出:
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
关键点:Java version 显示 17.0.20.1,说明系统自带 Maven 已正确使用 JDK 17,无需额外设置 JAVA_HOME。
二、前置条件
在开始安装前,请确认以下条件满足,否则后续步骤会因权限或网络问题中断。
- root 权限或 sudo:安装 JDK、Maven 需要写入系统目录(
/usr/lib/jvm、/usr/share/maven),创建/opt下的工作目录也需要相应权限。 - 网络可达:需要访问
downloads.apache.org(官方二进制包)、repo.maven.apache.org(Maven 中央仓库)与archive.apache.org(历史版本归档)。 - 磁盘空间:官方二进制包约 19 MB,加上本地 Maven 仓库缓存建议预留 ≥ 200 MB。
- 纯 Java 库:Commons Lang 是纯 Java 库,无本地原生依赖,x86_64 之外的架构同样可用。
验证三个下载源的连通性:
curl -sI https://downloads.apache.org/commons/lang/binaries/ | head -1
curl -sI https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/ | head -1
curl -sI https://archive.apache.org/dist/commons/lang/ | head -1
输出:
HTTP/1.1 200 OK
HTTP/2 200
HTTP/1.1 200 OK
⚠️ 注意:若内网无法直连上述域名,需按企业代理配置放行,否则 Maven 依赖与官方包均无法获取。本文实测环境为直连。
三、版本选型说明
Apache Commons Lang 是 Apache 基金会对 java.lang 的增强工具库(字符串、数组、布尔、元组、区间、反射 builder 等)。安装前先确认版本与坐标,避免引入错误构件——Lang 是「坐标陷阱」最多的 Commons 组件之一:2.x 与 3.x 使用完全不同的 Maven 坐标。
查询 Maven 中央仓库的全量版本列表:
curl -s https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/maven-metadata.xml
输出:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<versioning>
<latest>3.21.0</latest>
<release>3.21.0</release>
<versions>
<version>3.0</version>
<version>3.0.1</version>
<version>3.1</version>
<version>3.2</version>
<version>3.2.1</version>
<version>3.3</version>
<version>3.3.1</version>
<version>3.3.2</version>
<version>3.4</version>
<version>3.5</version>
<version>3.6</version>
<version>3.7</version>
<version>3.8</version>
<version>3.8.1</version>
<version>3.9</version>
<version>3.10</version>
<version>3.11</version>
<version>3.12.0</version>
<version>3.13.0</version>
<version>3.14.0</version>
<version>3.15.0</version>
<version>3.16.0</version>
<version>3.17.0</version>
<version>3.18.0</version>
<version>3.19.0</version>
<version>3.20.0</version>
<version>3.21.0</version>
</versions>
<lastUpdated>20260929180843</lastUpdated>
</versioning>
</metadata>
结论:latest = release = 3.21.0(2026-09-29 发布),3.x 全系共 27 个版本。
再查询官方发行目录的当前清单:
curl -s https://downloads.apache.org/commons/lang/binaries/
输出(HTML 目录页关键行,实测节选):
Index of /commons/lang/binaries (HTML 目录页,关键行如下)
commons-lang-2.6-bin.tar.gz 2013-03-03 21:07 3.3M
commons-lang-2.6-bin.tar.gz.asc 2013-03-03 21:07 203
commons-lang-2.6-bin.tar.gz.sha512 2019-04-14 22:43 129
commons-lang-2.6-bin.zip 2013-03-03 21:07 3.7M
commons-lang3-3.21.0-bin.tar.gz 2026-09-29 18:03 19M
commons-lang3-3.21.0-bin.tar.gz.asc 2026-09-29 18:03 265
commons-lang3-3.21.0-bin.tar.gz.sha512 2026-09-29 18:03 129
commons-lang3-3.21.0-bin.zip 2026-09-29 18:03 20M
解读:当前最新 3.21.0 与 2.x 末代 2.6 并存在同一 binaries 目录;2.x 老构件文件名前缀为 commons-lang-,3.x 为 commons-lang3-,与 Maven 坐标的断代完全对应。downloads.apache.org 只保留最新发行版与 2.6,历史版本在 archive.apache.org(实测归档目录含 2.4/2.5/2.6 与 3.0~3.21.0 全部 30 个版本的 -bin.tar.gz,目录总条目 250)。
两代版本对照与选型:
| 维度 | commons-lang 2.6(2.x 末代) | commons-lang3 3.21.0(当前线) |
|---|---|---|
| Maven 坐标 | commons-lang:commons-lang | org.apache.commons:commons-lang3 |
| 顶级包名 | org.apache.commons.lang | org.apache.commons.lang3 |
| 最低 Java | Java 1.2+(官方 pom 发行描述) | Java 8+(pom source/target 1.8,class major 52 实测) |
| 维护状态 | 2013-03 之后无新版,仅存量维护 | 2026-09-29 发布 3.21.0,活跃维护 |
| 二进制包文件名前缀 | commons-lang- | commons-lang3- |
| 典型能力 | 基础 StringUtils/ArrayUtils | 在 2.x 基础上增加 tuple/mutable/builder/Range,随机数 API 重构 |
关键差异与选型:新项目一律选 commons-lang3 3.21.0;只有维护依赖 2.x 的遗留系统时才继续使用 commons-lang 2.6。两者坐标不同,可以在同一工程内共存(见第八章实测),迁移期可渐进替换。
💡 技术拓展:Java 基础工具库不止 Commons Lang 一个,常见选项对比——
库 定位 代表 API 依赖 适用场景 Commons Lang java.lang的官方补集StringUtils、ArrayUtils、Pair、Range 零传递依赖 任何 Java 项目的基础工具层 Guava Google 核心库(集合/并发为主) Strings、Joiner、Splitter 单 jar 无传递依赖 已引入 Guava 的项目,集合与并发能力优先 Hutool 国产全家桶工具集 StrUtil、CollUtil 等 单 jar(生态模块较多) 快速开发、中小项目、偏好中文文档 本文选用 Commons Lang 3.21.0:零传递依赖、最低 Java 8 兼容面广、Apache 出品 API 稳定,是 Java 项目最保守也最通用的基础工具选择。
3.21.0 的最低 Java 版本要求,直接以官方 pom 为证:
curl -s https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.pom -o /tmp/lang3.pom
grep -n -i "release\|source\|target\|java.version" /tmp/lang3.pom | head -20
输出(实测节选):
36: The code is tested using the latest revision of the JDK for supported LTS releases: 8, 11, 17, 21, and 25.
41: Starting with Commons Lang 3.9, we target Java 8 and use those features.
122: <project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding>
126: <maven.compiler.source>1.8</maven.compiler.source>
127: <maven.compiler.target>1.8</maven.compiler.target>
142: <!-- Current 3.x release series -->
143: <commons.release.version>3.21.0</commons.release.version>
145: <commons.release.desc>(Java 8+)</commons.release.desc>
146: <!-- Previous 2.x release series -->
147: <commons.release.2.version>2.6</commons.release.2.version>
148: <commons.release.2.desc>(Requires Java 1.2 or later)</commons.release.2.desc>
再核实库自身字节码版本(运行环境要求 Java 8+ 的直接证据见 4.8 的 javap 实测:commons-lang3-3.21.0.jar 的 class major 为 52,即 Java 8 字节码)。
四、在线安装方案(A:Maven 依赖引入)
官方推荐方式:通过 Maven 引入 org.apache.commons:commons-lang3:3.21.0,由构建工具自动完成下载与 classpath 管理。适合可联网的开发/构建环境。
4.1 方案说明与适用场景
本方案先确认 JDK 与 Maven 就绪(第一章已实测),再创建一个最小 Maven 工程引入依赖,最后用真实工具类调用完成编译与运行验证。适合持续集成、可访问中央仓库的场景。
4.2 确认 JDK 与 Maven
JDK 与 Maven 的版本验证已在第一章完成(java -version 显示 17.0.20.1、mvn -v 显示 Maven 3.6.3 且 Java version 为 17)。若系统尚未安装,Ubuntu 22.04 可执行 DEBIAN_FRONTEND=noninteractive apt-get install -y openjdk-17-jdk maven 安装,安装后以上述验证命令的回显为准。
⚠️ 注意:Commons Lang 3.21.0 本身只要求 Java 8+,本文示例工程按 release 17 编译,因此需要 JDK 17;若你的工程按 release 8 编译,JDK 8 亦可直接使用该库(库本身即 Java 8 字节码)。
4.3 创建 Maven 工程并引入依赖
创建工程目录:
mkdir -p /opt/lang-demo/src/main/java/demo
/opt/lang-demo/pom.xml 全文:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>lang-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.21.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>17</release>
<compilerArgs>
<arg>-Xlint:deprecation</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
逐项说明:
project.build.sourceEncoding=UTF-8:源码编码。示例代码含中文注释/字符串,必须显式声明,否则编译器按平台默认编码(本环境为 ANSI_X3.4-1968)处理会报unmappable character(排障实录见 FAQ Q4)。maven.compiler.release=17:用 JDK 17 语义产出字节码;要求 Maven 运行在 JDK 17 上(本环境实测满足)。dependencies:仅声明commons-lang3:3.21.0一项。maven-compiler-plugin显式固定 3.11.0 并加-Xlint:deprecation,用于暴露 3.21.0 的废弃 API 告警(见 4.6 与第九章 Q5)。
4.4 预拉取依赖构件
首次构建前可单独拉取一次依赖,观察真实下载过程:
cd /opt/lang-demo && mvn -B dependency:get -Dartifact=org.apache.commons:commons-lang3:3.21.0
输出节选(实测尾部):
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar (749 kB at 23 kB/s)
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/net/bytebuddy/byte-buddy/1.17.5/byte-buddy-1.17.5.jar (9.0 MB at 241 kB/s)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 04:39 min
[INFO] Finished at: 2026-10-14T15:15:52Z
[INFO] ------------------------------------------------------------------------
解读:核心 jar 为 749 kB;dependency:get 会连 test scope 的传递依赖(如 byte-buddy,来自 mockito 等测试依赖)一并拉取,但 compile/runtime 依赖树仍为空,下一节用 dependency:tree 证实。
4.5 编写最小验证程序
/opt/lang-demo/src/main/java/demo/StringUtilsDemo.java 全文:
package demo;
import java.util.Arrays;
import org.apache.commons.lang3.StringUtils;
public class StringUtilsDemo {
public static void main(String[] args) {
System.out.println("== null 安全约定 ==");
System.out.println("isEmpty(null) = " + StringUtils.isEmpty(null));
System.out.println("isEmpty(\"\") = " + StringUtils.isEmpty(""));
System.out.println("isEmpty(\" \") = " + StringUtils.isEmpty(" "));
System.out.println("isBlank(null) = " + StringUtils.isBlank(null));
System.out.println("isBlank(\"\") = " + StringUtils.isBlank(""));
System.out.println("isBlank(\" \") = " + StringUtils.isBlank(" "));
System.out.println("isNotBlank(\" a \") = " + StringUtils.isNotBlank(" a "));
System.out.println("== join ==");
System.out.println("join([a,b,c], '-') = " + StringUtils.join(new String[]{"a", "b", "c"}, '-'));
System.out.println("join([1,2,3], ',') = " + StringUtils.join(new int[]{1, 2, 3}, ','));
System.out.println("== split ==");
String[] parts = StringUtils.split("a,b,,c", ',');
System.out.println("split(\"a,b,,c\", ',') = " + Arrays.toString(parts));
System.out.println("split(null, ',') = " + Arrays.toString(StringUtils.split(null, ',')));
System.out.println("splitPreserveAllTokens(\"a,b,,c\", ',') = " + Arrays.toString(StringUtils.splitPreserveAllTokens("a,b,,c", ',')));
System.out.println("== abbreviate ==");
System.out.println("abbreviate(\"abcdefg\", 10) = " + StringUtils.abbreviate("abcdefg", 10));
System.out.println("abbreviate(\"abcdefg\", 6) = " + StringUtils.abbreviate("abcdefg", 6));
try {
StringUtils.abbreviate("abcdefg", 3);
} catch (IllegalArgumentException e) {
System.out.println("abbreviate(\"abcdefg\", 3) 抛出 IllegalArgumentException: " + e.getMessage());
}
System.out.println("== substringBetween ==");
System.out.println("substringBetween(\"<a>text</a>\", \"<a>\", \"</a>\") = " + StringUtils.substringBetween("<a>text</a>", "<a>", "</a>"));
System.out.println("== overlay ==");
System.out.println("overlay(\"abcdef\", \"***\", 2, 5) = " + StringUtils.overlay("abcdef", "***", 2, 5));
System.out.println("== left / right / mid ==");
System.out.println("left(\"abcdef\", 3) = " + StringUtils.left("abcdef", 3));
System.out.println("right(\"abcdef\", 3) = " + StringUtils.right("abcdef", 3));
System.out.println("mid(\"abcdef\", 2, 3) = " + StringUtils.mid("abcdef", 2, 3));
System.out.println("left(null, 3) = " + StringUtils.left(null, 3));
System.out.println("right(\"abc\", 10) = " + StringUtils.right("abc", 10));
System.out.println("== capitalize / defaultString ==");
System.out.println("capitalize(\"hello\") = " + StringUtils.capitalize("hello"));
System.out.println("capitalize(null) = " + StringUtils.capitalize(null));
System.out.println("defaultString(null) = [" + StringUtils.defaultString(null) + "]");
System.out.println("defaultString(null, \"N/A\") = " + StringUtils.defaultString(null, "N/A"));
}
}
说明:程序覆盖 StringUtils 的 null 安全约定、join/split、缩写、截取与首字母大写等高频用法,作为依赖引入是否成功的真实验证。
4.6 编译验证
cd /opt/lang-demo && mvn -B compile
输出节选(实测,compile 段与结尾):
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ lang-demo ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 6 source files with javac [debug release 17] to target/classes
[WARNING] /opt/lang-demo/src/main/java/demo/RandomDemo.java:[9,74] nextInt(int,int) in org.apache.commons.lang3.RandomUtils has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/RandomDemo.java:[10,79] nextDouble(double,double) in org.apache.commons.lang3.RandomUtils has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/RandomDemo.java:[11,72] nextBoolean() in org.apache.commons.lang3.RandomUtils has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/RandomDemo.java:[14,90] randomAlphabetic(int) in org.apache.commons.lang3.RandomStringUtils has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/RandomDemo.java:[15,87] randomNumeric(int) in org.apache.commons.lang3.RandomStringUtils has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/TupleRangeDemo.java:[29,33] <T>between(T,T) in org.apache.commons.lang3.Range has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/TupleRangeDemo.java:[38,34] <T>between(T,T) in org.apache.commons.lang3.Range has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/TupleRangeDemo.java:[41,34] <T>between(T,T) in org.apache.commons.lang3.Range has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/StringUtilsDemo.java:[54,75] defaultString(java.lang.String,java.lang.String) in org.apache.commons.lang3.StringUtils has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/ExceptionSystemDemo.java:[37,75] <T>defaultIfNull(T,T) in org.apache.commons.lang3.ObjectUtils has been deprecated
[WARNING] /opt/lang-demo/src/main/java/demo/ExceptionSystemDemo.java:[52,46] getValue() in org.apache.commons.lang3.mutable.MutableInt has been deprecated
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.318 s
[INFO] Finished at: 2026-10-14T15:23:18Z
[INFO] ------------------------------------------------------------------------
说明:实测工程包含 6 个演示类(后续章节与《Apache Commons Lang 3.21.0 常用 API 与工具类详解》《Apache Commons Lang 3.21.0 进阶 API 与废弃迁移详解》两篇的示例同源),读者按本文操作只建 1 个类时显示 Compiling 1 source files 属正常。WARNING 均为 3.21.0 的废弃 API 告警(-Xlint:deprecation 全量暴露),不影响运行,迁移写法见《Apache Commons Lang 3.21.0 进阶 API 与废弃迁移详解》。
4.7 验证零传递依赖
cd /opt/lang-demo && mvn -B dependency:tree
输出节选(实测):
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ lang-demo ---
[INFO] demo:lang-demo:jar:1.0.0
[INFO] \- org.apache.commons:commons-lang3:jar:3.21.0:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
结论:依赖树中只有 commons-lang3 自己,零传递依赖。这也是它适合作为基础工具库引入任何工程的原因——不会拖入任何其他版本族。
4.8 核实库自身字节码版本
cd /opt/lang-demo
javap -v -cp target/classes demo.StringUtilsDemo | grep major
javap -v -cp /root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar org.apache.commons.lang3.StringUtils | grep major
输出:
major version: 61
major version: 52
解读:本项目类 major 61(Java 17 字节码);commons-lang3 3.21.0 库自身 class major 52(Java 8 字节码),与官方 pom 的 source/target 1.8 一致。运行环境 Java 8+ 即可加载该库,编译进 Java 8 项目也不会报 UnsupportedClassVersionError。
4.9 运行验证
cd /opt/lang-demo
CP=target/classes:/root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
java -Dfile.encoding=UTF-8 -cp $CP demo.StringUtilsDemo
输出(实测,-Dfile.encoding=UTF-8 的作用见第七章):
== null 安全约定 ==
isEmpty(null) = true
isEmpty("") = true
isEmpty(" ") = false
isBlank(null) = true
isBlank("") = true
isBlank(" ") = true
isNotBlank(" a ") = true
== join ==
join([a,b,c], '-') = a-b-c
join([1,2,3], ',') = 1,2,3
== split ==
split("a,b,,c", ',') = [a, b, c]
split(null, ',') = null
splitPreserveAllTokens("a,b,,c", ',') = [a, b, , c]
== abbreviate ==
abbreviate("abcdefg", 10) = abcdefg
abbreviate("abcdefg", 6) = abc...
abbreviate("abcdefg", 3) 抛出 IllegalArgumentException: Minimum abbreviation width is 4
== substringBetween ==
substringBetween("<a>text</a>", "<a>", "</a>") = text
== overlay ==
overlay("abcdef", "***", 2, 5) = ab***f
== left / right / mid ==
left("abcdef", 3) = abc
right("abcdef", 3) = def
mid("abcdef", 2, 3) = cde
left(null, 3) = null
right("abc", 10) = abc
== capitalize / defaultString ==
capitalize("hello") = Hello
capitalize(null) = null
defaultString(null) = []
defaultString(null, "N/A") = N/A
验证点:输出全部符合 StringUtils 的 null 安全约定(详见《Apache Commons Lang 3.21.0 常用 API 与工具类详解》),依赖已正确引入并可正常运行。
4.10 与官方二进制包的一致性核对
对本地仓库中的 jar 计算哈希,稍后与方案 B 解压出的 jar 对比:
sha256sum /root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
输出:
a2af247ec4db6eb8947e18e23e0433e4871a7c5d0a40e1f92e46ede54ae1defe /root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
该值将在 5.4 与官方二进制包内的 jar 逐字符比对。
五、离线安装方案(B:官方二进制包)
适合内网隔离、无法访问中央仓库的环境。官方 bin 包零依赖,解压取 jar 即用,无需补任何第三方 jar。
5.1 方案说明与适用场景
官方 commons-lang3-3.21.0-bin.tar.gz 内含核心 jar、sources/javadoc/tests jar 与离线 apidocs。由于 3.21.0 零传递依赖,本方案不需要补任何依赖 jar,流程为:下载 → sha512 校验 → 解压 → javac/java -cp 直跑验证。
5.2 下载与校验
mkdir -p /opt/lang-jars && cd /opt/lang-jars
curl -fL -O https://downloads.apache.org/commons/lang/binaries/commons-lang3-3.21.0-bin.tar.gz
curl -fL -O https://downloads.apache.org/commons/lang/binaries/commons-lang3-3.21.0-bin.tar.gz.sha512
stat -c "%n %s bytes" commons-lang3-3.21.0-bin.tar.gz
sha512sum commons-lang3-3.21.0-bin.tar.gz
cat commons-lang3-3.21.0-bin.tar.gz.sha512
输出(实测,curl 进度条为下载全程真实回显):
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 18.7M 100 18.7M 0 0 157k 0 0:02:02 0:02:02 --:--:-- 148k
commons-lang3-3.21.0-bin.tar.gz 19705282 bytes
1c225a75b66946adc007ca774e59671b9c8ac7c0a9f17977829b4530121fadd322ff179a54a9e7d2d4f8bd1095071fe3ee9b8da7902a82351f8b8ce4d530f129 commons-lang3-3.21.0-bin.tar.gz
1c225a75b66946adc007ca774e59671b9c8ac7c0a9f17977829b4530121fadd322ff179a54a9e7d2d4f8bd1095071fe3ee9b8da7902a82351f8b8ce4d530f129
验证点:包体积 19,705,282 字节(18.7M),sha512sum 计算值与官方 .sha512 文件逐字符一致,包完整可信。
5.3 解压与目录结构
cd /opt/lang-jars && tar xzf commons-lang3-3.21.0-bin.tar.gz && ls -la
输出:
total 19260
drwxr-xr-x 3 root root 4096 Oct 14 15:24 .
drwxr-xr-x 1 root root 4096 Oct 14 15:07 ..
drwxr-xr-x 3 root root 4096 Oct 14 15:24 commons-lang3-3.21.0
-rw-r--r-- 1 root root 19705282 Oct 14 15:04 commons-lang3-3.21.0-bin.tar.gz
-rw-r--r-- 1 root root 129 Oct 14 15:04 commons-lang3-3.21.0-bin.tar.gz.sha512
cd /opt/lang-jars/commons-lang3-3.21.0 && ls -la
输出:
total 12888
drwxr-xr-x 3 root root 4096 Sep 25 19:47 .
drwxr-xr-x 3 root root 4096 Oct 14 15:24 ..
-rw-r--r-- 1 root root 7107 Sep 25 19:47 CONTRIBUTING.md
-rw-r--r-- 1 root root 11359 Sep 25 19:47 LICENSE.txt
-rw-r--r-- 1 root root 174 Sep 25 19:47 NOTICE.txt
-rw-r--r-- 1 root root 7237 Sep 25 19:47 README.md
-rw-r--r-- 1 root root 196901 Sep 25 19:47 RELEASE-NOTES.txt
drwxr-xr-x 7 root root 4096 Sep 25 19:47 apidocs
-rw-r--r-- 1 root root 8791180 Sep 25 19:47 commons-lang3-3.21.0-javadoc.jar
-rw-r--r-- 1 root root 811443 Sep 25 19:47 commons-lang3-3.21.0-sources.jar
-rw-r--r-- 1 root root 828131 Sep 25 19:47 commons-lang3-3.21.0-test-sources.jar
-rw-r--r-- 1 root root 1758136 Sep 25 19:47 commons-lang3-3.21.0-tests.jar
-rw-r--r-- 1 root root 749083 Sep 25 19:47 commons-lang3-3.21.0.jar
解读:核心运行 jar 是 commons-lang3-3.21.0.jar(749,083 字节);apidocs/ 目录与 -javadoc.jar 提供离线 API 文档,-sources.jar 可在 IDE 中直接反查源码——离线排查 API 问题时非常有用。
5.4 jar 内容与一致性核对
查看核心 jar 的顶层条目:
cd /opt/lang-jars/commons-lang3-3.21.0 && jar tf commons-lang3-3.21.0.jar | head -12
输出:
META-INF/
META-INF/MANIFEST.MF
org/
org/apache/
org/apache/commons/
org/apache/commons/lang3/
org/apache/commons/lang3/arch/
org/apache/commons/lang3/builder/
org/apache/commons/lang3/compare/
org/apache/commons/lang3/concurrent/
org/apache/commons/lang3/concurrent/locks/
org/apache/commons/lang3/event/
与方案 A 的 Maven Central 构件做 sha256 一致性比对:
cd /opt/lang-jars/commons-lang3-3.21.0
stat -c "%n %s" commons-lang3-3.21.0.jar /root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
sha256sum commons-lang3-3.21.0.jar /root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
输出:
commons-lang3-3.21.0.jar 749083
/root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar 749083
a2af247ec4db6eb8947e18e23e0433e4871a7c5d0a40e1f92e46ede54ae1defe commons-lang3-3.21.0.jar
a2af247ec4db6eb8947e18e23e0433e4871a7c5d0a40e1f92e46ede54ae1defe /root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
结论:官方 tar 包内 jar 与 Maven Central 构件字节数相同(749,083)、sha256 逐字符一致,两种方式拿到的是同一个构件。
5.5 手动编译运行验证
复用 4.5 节的 StringUtilsDemo.java,以 jar 目录作为 classpath 直接编译运行:
cd /opt/lang-jars
cp /opt/lang-demo/src/main/java/demo/StringUtilsDemo.java /opt/lang-jars/StringUtilsDemo.java
mkdir -p bin-classes
javac -encoding UTF-8 -cp /opt/lang-jars/commons-lang3-3.21.0/commons-lang3-3.21.0.jar -d bin-classes StringUtilsDemo.java
java -Dfile.encoding=UTF-8 -cp bin-classes:/opt/lang-jars/commons-lang3-3.21.0/commons-lang3-3.21.0.jar demo.StringUtilsDemo
输出(实测):
Note: StringUtilsDemo.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
== null 安全约定 ==
isEmpty(null) = true
isEmpty("") = true
isEmpty(" ") = false
isBlank(null) = true
isBlank("") = true
isNotBlank(" a ") = true
== join ==
join([a,b,c], '-') = a-b-c
join([1,2,3], ',') = 1,2,3
== split ==
split("a,b,,c", ',') = [a, b, c]
split(null, ',') = null
splitPreserveAllTokens("a,b,,c", ',') = [a, b, , c]
== abbreviate ==
abbreviate("abcdefg", 10) = abcdefg
abbreviate("abcdefg", 6) = abc...
abbreviate("abcdefg", 3) 抛出 IllegalArgumentException: Minimum abbreviation width is 4
== substringBetween ==
substringBetween("<a>text</a>", "<a>", "</a>") = text
== overlay ==
overlay("abcdef", "***", 2, 5) = ab***f
== left / right / mid ==
left("abcdef", 3) = abc
right("abcdef", 3) = def
mid("abcdef", 2, 3) = cde
left(null, 3) = null
right("abc", 10) = abc
== capitalize / defaultString ==
capitalize("hello") = Hello
capitalize(null) = null
defaultString(null) = []
defaultString(null, "N/A") = N/A
验证点:运行输出与方案 A 逐行一致。javac 的 Note 提示来自源码调用的废弃重载 defaultString(String,String)(不加 -Xlint:deprecation 也会有这条 Note),功能不受影响。
六、方案对比与选择建议
两种方案各有取舍,本节汇总关键差异并给出选型建议。
| 维度 | 方案 A:Maven 依赖 | 方案 B:官方二进制包 |
|---|---|---|
| 是否需联网 | 构建时需联网(首次下载 749 kB) | 仅下载阶段需联网(19 MB 一次) |
| 安装方式 | 声明坐标自动解析 | 手动下载 + 校验 + 解压 |
| 依赖完整性 | 自动管理(零传递依赖) | 零依赖,取 jar 即用 |
| 附带产物 | 仅 jar | jar + sources + javadoc + tests + apidocs |
| 升级方式 | 改 pom 版本号 | 重新下载解压 |
| 适用场景 | CI、可联网开发、多模块工程 | 内网隔离、离线运行、快速验证 |
选择建议:可联网优先方案 A(坐标即版本,升级只改一个数字);内网隔离或临时验证选方案 B(解压即用,且自带全套离线文档)。两者 jar 的 sha256 实测一致,行为完全相同,可按场景互换。
七、基础配置说明
Commons Lang 是进程内库,没有任何配置文件(无 properties/yaml/xml),所有「配置」都集中在编译期编码与运行期 JVM 参数两处,本节逐项说明。
7.1 编译期编码
- Maven 工程:由 pom 的
project.build.sourceEncoding=UTF-8统一控制(4.3 已设置)。 - javac 手工编译:必须显式加
-encoding UTF-8,否则在 POSIX locale(平台编码 ANSI_X3.4-1968)下编译含中文源码报unmappable character(排障实录见 FAQ Q4)。
7.2 运行期编码
先看本环境的默认 locale 与 Maven 平台编码:
locale
mvn -v | tail -2
输出(实测节选):
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "6.8.0-138-generic", arch: "amd64", family: "unix"
platform encoding: ANSI_X3.4-1968 即 ASCII。此时直接运行含中文 println 的类,输出中文会变成问号(排障实录见 FAQ Q3)。解决办法是运行时统一加 -Dfile.encoding=UTF-8:
cd /opt/lang-demo
CP=target/classes:/root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
java -Dfile.encoding=UTF-8 -cp $CP demo.StringUtilsDemo | head -1
输出:
== null 安全约定 ==
对比不加该参数的输出见 FAQ Q3。本文所有运行命令统一带 -Dfile.encoding=UTF-8。
7.3 最小工程结构
方案 A 建成的工程结构如下(find 实测):
cd /opt/lang-demo && find . -type f -not -path "./target/*" | sort
输出:
./pom.xml
./src/main/java/demo/BooleanArrayDemo.java
./src/main/java/demo/BuilderDemo.java
./src/main/java/demo/ExceptionSystemDemo.java
./opt/lang-demo/src/main/java/demo/RandomDemo.java
./src/main/java/demo/StringUtilsDemo.java
./src/main/java/demo/TupleRangeDemo.java
⚠️ 注意:以上为实测工程全量文件(含后续章节示例)。最小可用结构只需
pom.xml+ 一个演示类(如./src/main/java/demo/StringUtilsDemo.java)。
八、升级、降级与多版本管理
本节覆盖三个真实场景:升级到新版、临时回退旧版、以及多版本共存规则。所有操作均实测。
8.1 升级与降级:只改 pom 版本号
Maven 方式下升级/降级就是改 pom.xml 中 <version> 一行。以「3.21.0 → 3.20.0」的降级为例(升级方向同理)。先确保 3.20.0 已入本地仓库:
mvn -B dependency:get -Dartifact=org.apache.commons:commons-lang3:3.20.0
输出节选(实测尾部):
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 43.813 s
[INFO] Finished at: 2026-10-14T15:50:43Z
[INFO] ------------------------------------------------------------------------
临时把 pom 版本改为 3.20.0 并验证解析结果:
cd /opt/lang-demo
cp pom.xml pom.xml.bak
sed -i "s|<version>3.21.0</version>|<version>3.20.0</version>|" pom.xml
grep -A2 "<artifactId>commons-lang3</artifactId>" pom.xml | grep version
mvn -B dependency:tree 2>/dev/null | grep -E "commons-lang3|BUILD"
mv pom.xml.bak pom.xml
输出(实测):
<version>3.20.0</version>
[INFO] \- org.apache.commons:commons-lang3:jar:3.20.0:compile
[INFO] BUILD SUCCESS
验证点:dependency:tree 显示 3.20.0 已生效;最后一条命令已把 pom 恢复为 3.21.0。升级同理——把版本改成目标版本后执行 mvn -B compile,Maven 自动下载新版本构件。
8.2 同一 classpath 只能放一个 lang3 版本
3.x 各版本共用同一个 Maven 坐标与同一个包名 org.apache.commons.lang3,同一 classpath 上放两个 3.x 版本时,JVM 按classpath 顺序取第一个命中者,排序不同结果不同,属未定义用法。实测证明(验证类 ClasspathOrder.java 放在 /opt/lang-jars,全文):
import org.apache.commons.lang3.StringUtils;
/** 打印 classpath 上实际加载到的 commons-lang3 版本 */
public class ClasspathOrder {
public static void main(String[] args) {
System.out.println("实际加载的版本 = " + StringUtils.class.getPackage().getImplementationVersion());
}
}
编译:
cd /opt/lang-jars
javac -encoding UTF-8 -cp /root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar -d . ClasspathOrder.java
按两种顺序运行:
M2=/root/.m2/repository/org/apache/commons/commons-lang3
java -Dfile.encoding=UTF-8 -cp .:$M2/3.20.0/commons-lang3-3.20.0.jar:$M2/3.21.0/commons-lang3-3.21.0.jar ClasspathOrder
java -Dfile.encoding=UTF-8 -cp .:$M2/3.21.0/commons-lang3-3.21.0.jar:$M2/3.20.0/commons-lang3-3.20.0.jar ClasspathOrder
输出(实测,两行分别是两条命令的回显):
实际加载的版本 = 3.20.0
实际加载的版本 = 3.21.0
结论:classpath 上谁排前谁生效。因此多版本管理的正确做法是:一个工程(一个 classpath)只声明一个 3.x 版本,用 Maven 的版本号切换;本地仓库可以同时缓存多个版本目录而互不干扰:
ls /root/.m2/repository/org/apache/commons/commons-lang3
输出:
3.20.0
3.21.0
8.3 2.x 与 3.x 共存:坐标与包名都不同
2.x 坐标是 commons-lang:commons-lang、包名是 org.apache.commons.lang,与 3.x 完全错开,可以放在同一个 classpath 中共存——这是遗留系统渐进迁移的基础。实测:先下载 2.x 官方二进制包并解压:
cd /opt/lang-jars
curl -fL -sO https://downloads.apache.org/commons/lang/binaries/commons-lang-2.6-bin.tar.gz
ls -l commons-lang-2.6-bin.tar.gz
tar xzf commons-lang-2.6-bin.tar.gz
ls commons-lang-2.6/*.jar
输出(实测):
-rw-r--r-- 1 root root 3418617 Oct 14 15:51 commons-lang-2.6-bin.tar.gz
/opt/lang-jars/commons-lang-2.6/commons-lang-2.6-javadoc.jar
/opt/lang-jars/commons-lang-2.6/commons-lang-2.6-sources.jar
/opt/lang-jars/commons-lang-2.6/commons-lang-2.6.jar
再写一个同时调用两个包名的验证类 CoexistDemo.java(全文):
import org.apache.commons.lang.StringUtils;
/** 2.x 与 3.x 同 classpath 共存:两个包名互不冲突 */
public class CoexistDemo {
public static void main(String[] args) {
System.out.println("lang2 isEmpty(\"\") = " + StringUtils.isEmpty(""));
System.out.println("lang2 version = " + StringUtils.class.getPackage().getImplementationVersion());
System.out.println("lang3 capitalize = " + org.apache.commons.lang3.StringUtils.capitalize("hello"));
System.out.println("lang3 version = " + org.apache.commons.lang3.StringUtils.class.getPackage().getImplementationVersion());
}
}
编译并运行(两个 jar 同时在 classpath 上):
cd /opt/lang-jars
javac -encoding UTF-8 -cp commons-lang-2.6/commons-lang-2.6.jar:commons-lang3-3.21.0/commons-lang3-3.21.0.jar -d . CoexistDemo.java
java -Dfile.encoding=UTF-8 -cp .:commons-lang-2.6/commons-lang-2.6.jar:commons-lang3-3.21.0/commons-lang3-3.21.0.jar CoexistDemo
输出(实测):
COMPILED-OK
lang2 isEmpty("") = true
lang2 version = 2.6
lang3 capitalize = Hello
lang3 version = 3.21.0
结论:编译无告警、运行两套 API 各自生效(2.6 与 3.21.0 同时可加载)。迁移期间可让新旧代码并存,逐步把 org.apache.commons.lang 的 import 替换为 org.apache.commons.lang3(注意个别 API 签名有差异,如 2.x 的 StringEscapeUtils 已在 3.x 废弃并迁往 commons-text)。
九、常见问题(FAQ)
Q1:mvn compile 报 Fatal error compiling: invalid flag: --release 怎么办?
Maven 实际使用的 JDK 低于 9 所致:pom 配置的 --release 17 参数从 JDK 9 才引入 javac,JDK 8 的 javac 不认识该参数。实测报错原文(捕自 Maven 运行在 JDK 8 上的环境,与 maven-compiler-plugin 3.11.0 + release 17 配置组合):
[INFO] Compiling 6 source files with javac [debug release 17] to target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.328 s
[INFO] Finished at: 2026-10-14T15:26:46Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project lang-demo: Fatal error compiling: invalid flag: --release -> [Help 1]
定位方法:mvn -v | head -3 查看 Java version: 行——它才是 Maven 实际使用的 JDK(不是 java -version 的结果)。解决:把 JAVA_HOME 指向 JDK 17 后重新执行 mvn compile(多 JDK 并存的完整切换步骤见《CentOS 8 安装 Apache Commons Lang 指南》);或把示例工程的 maven.compiler.release 降到与当前 JDK 匹配的值(Commons Lang 本身 Java 8 即可用)。
Q2:编译报 cannot find symbol: class Triple(或其他类)怎么办?
九成是 import 写错了包位置。Triple/Pair 在 org.apache.commons.lang3.tuple 子包(不在 lang3 顶层),MutableInt 在 org.apache.commons.lang3.mutable。实测把 Triple 写成顶层包时的完整报错:
/opt/lang-jars/badimport/BadImport.java:1: error: cannot find symbol
import org.apache.commons.lang3.Triple;
^
symbol: class Triple
location: package org.apache.commons.lang3
/opt/lang-jars/badimport/BadImport.java:5: error: cannot find symbol
Triple<String, Integer, Boolean> t = Triple.of("flag", 1, true);
^
symbol: class Triple
location: class BadImport
/opt/lang-jars/badimport/BadImport.java:5: error: cannot find symbol
Triple<String, Integer, Boolean> t = Triple.of("flag", 1, true);
^
symbol: variable Triple
location: class BadImport
3 errors
定位工具是 jar tf——直接在 jar 里搜类名确认真实包路径:
jar tf /root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar | grep -iE "triple|/Pair"
输出:
org/apache/commons/lang3/tuple/ImmutableTriple.class
org/apache/commons/lang3/tuple/MutableTriple.class
org/apache/commons/lang3/tuple/Pair.class
org/apache/commons/lang3/tuple/Triple.class
把 import 改为 org.apache.commons.lang3.tuple.Triple 即可。
Q3:运行含中文的 println 输出全是问号 ??? 怎么办?
POSIX locale(平台编码 ANSI_X3.4-1968)下,JVM 默认字符集是 ASCII,中文字符编码失败被替换为 ?。实测不加参数直接运行 4.5 的演示类:
cd /opt/lang-demo
CP=target/classes:/root/.m2/repository/org/apache/commons/commons-lang3/3.21.0/commons-lang3-3.21.0.jar
java -cp $CP demo.StringUtilsDemo
输出节选(实测首部,对比 4.9 正常输出):
== null ???? ==
isEmpty(null) = true
isEmpty("") = true
isEmpty(" ") = false
isBlank(null) = true
isBlank("") = true
isNotBlank(" a ") = true
== join ==
join([a,b,c], '-') = a-b-c
join([1,2,3], ',') = 1,2,3
== split ==
解决:运行时加 -Dfile.encoding=UTF-8(4.9 与 7.2 的写法),或把系统 locale 改为 UTF-8(如 export LANG=C.UTF-8)。该参数只影响运行期输出编码,与编译期编码(7.1)互相独立,两者都要设置。
Q4:javac 编译含中文源码报 unmappable character (0xE6) for encoding US-ASCII 怎么办?
手工 javac 编译时未指定源码编码,javac 按平台默认编码(US-ASCII)读取 UTF-8 中文源码导致。实测报错节选:
ClasspathOrder.java:3: error: unmappable character (0xE6) for encoding US-ASCII
/** ?????? classpath ????????????????????? commons-lang3 ?????? */
^
ClasspathOrder.java:3: error: unmappable character (0x89) for encoding US-ASCII
^
ClasspathOrder.java:6: error: unmappable character (0xE5) for encoding US-ASCII
System.out.println("????????????????????? = " + StringUtils.class.getPackage().getImplementationVersion());
^
54 errors
解决:javac 显式加 -encoding UTF-8(8.2、8.3 的命令均按此写法)。Maven 工程则由 pom 的 project.build.sourceEncoding=UTF-8 覆盖,无需手工处理。
Q5:编译出现大量 has been deprecated WARNING 怎么办?
这是 3.21.0 的废弃 API 告警(4.6 实测共 11 条,全部由 -Xlint:deprecation 暴露),功能仍正常、不影响运行。要点:RandomStringUtils/RandomUtils 的旧静态方法、Range.between(T,T)、StringUtils.defaultString(String,String)、ObjectUtils.defaultIfNull、MutableInt.getValue() 均已 @Deprecated。新写法(如 RandomStringUtils.insecure().nextAlphabetic(8))与逐项迁移表见《Apache Commons Lang 3.21.0 进阶 API 与废弃迁移详解》。
十、卸载与清理
Commons Lang 不做系统级安装,删除工作目录与本地仓库缓存即可完全清理:
rm -rf /opt/lang-demo /opt/lang-jars
ls -d /opt/lang-demo /opt/lang-jars 2>/dev/null || echo "lang-demo lang-jars removed"
输出:
lang-demo lang-jars removed
如需连依赖缓存一起清理:
rm -rf /root/.m2/repository/org/apache/commons/commons-lang3 /root/.m2/repository/org/apache/commons/commons-lang
如需同时移除 JDK 与 Maven 系统包:DEBIAN_FRONTEND=noninteractive apt-get remove -y openjdk-17-jdk maven(仅移除软件包本身)。
十一、参考链接
- Apache Commons Lang 官方站点:https://commons.apache.org/proper/commons-lang/
- 官方发行下载目录(binaries/、source/):https://downloads.apache.org/commons/lang/
- 历史版本归档目录:https://archive.apache.org/dist/commons/lang/
- Maven 中央仓库坐标页:https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/
- Apache Commons Lang 3.21.0 Javadoc:https://commons.apache.org/proper/commons-lang/javadocs/api-release/
- Commons Lang 3.21.0 发布说明(官方包内 RELEASE-NOTES.txt 同内容):https://commons.apache.org/proper/commons-lang/changes-report.html