Ubuntu 22.04 安装 Apache Commons Lang 指南

在 Ubuntu 22.04 上从零引入 Apache Commons Lang(commons-lang3)3.21.0,覆盖版本选型与 2.6 断代对照、Maven 在线依赖与官方二进制包两套方案、多版本管理,附真实回显与排障。

Apache Commons LangApache Commons Lang2026/10/15

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。

二、前置条件

在开始安装前,请确认以下条件满足,否则后续步骤会因权限或网络问题中断。

  1. root 权限或 sudo:安装 JDK、Maven 需要写入系统目录(/usr/lib/jvm、/usr/share/maven),创建 /opt 下的工作目录也需要相应权限。
  2. 网络可达:需要访问 downloads.apache.org(官方二进制包)、repo.maven.apache.org(Maven 中央仓库)与 archive.apache.org(历史版本归档)。
  3. 磁盘空间:官方二进制包约 19 MB,加上本地 Maven 仓库缓存建议预留 ≥ 200 MB。
  4. 纯 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-langorg.apache.commons:commons-lang3
顶级包名org.apache.commons.langorg.apache.commons.lang3
最低 JavaJava 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 Langjava.lang 的官方补集StringUtils、ArrayUtils、Pair、Range零传递依赖任何 Java 项目的基础工具层
GuavaGoogle 核心库(集合/并发为主)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 即用
附带产物仅 jarjar + 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(仅移除软件包本身)。

十一、参考链接

  1. Apache Commons Lang 官方站点:https://commons.apache.org/proper/commons-lang/
  2. 官方发行下载目录(binaries/、source/):https://downloads.apache.org/commons/lang/
  3. 历史版本归档目录:https://archive.apache.org/dist/commons/lang/
  4. Maven 中央仓库坐标页:https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/
  5. Apache Commons Lang 3.21.0 Javadoc:https://commons.apache.org/proper/commons-lang/javadocs/api-release/
  6. Commons Lang 3.21.0 发布说明(官方包内 RELEASE-NOTES.txt 同内容):https://commons.apache.org/proper/commons-lang/changes-report.html