CentOS 8 安装 Apache Commons Digester 指南

在 CentOS 8 从零搭建 Apache Commons Digester 使用环境,将默认 Java 1.8 切换到 JDK 17、安装 Maven 3.5.4,给出 Maven 依赖与官方发行包两种获取 jar 的方案。

Apache Commons DigesterApache Commons Digester2026/10/14

CentOS 8 安装 Apache Commons Digester 指南

目标:在一台全新的 CentOS 8 服务器上搭建可编译、可运行 Apache Commons Digester 的 Java 环境,并处理 CentOS 8 默认 Java 版本过低的问题。全部命令与回显已在 CentOS Linux 8(JDK 17 + Maven 3.5.4)实测通过。

一、环境信息

本章先确认操作系统版本,再安装 JDK 17 与 Maven。CentOS 8 的一个关键差异是:系统里可能同时装了 Java 8 与 Java 17,java/javac 命令默认可能仍指向 1.8,必须显式切换。Commons Digester 3.2 的字节码目标是 Java 5(见「版本选型」章节的清单证据),JDK 8 及以上均可运行,本文统一使用 JDK 17。

先确认系统版本:

cat /etc/os-release | grep -E "^(NAME|VERSION)="

实测回显:

NAME="CentOS Linux"
VERSION="8"

查看系统里已存在的 Java 与 Maven 包:

dnf list installed | grep -Ei "java-17-openjdk|java-1.8.0-openjdk|^maven"

实测回显(输出节选,实测首尾):

java-1.8.0-openjdk.x86_64                 1:1.8.0.312.b07-2.el8_5   @AppStream
java-1.8.0-openjdk-devel.x86_64           1:1.8.0.312.b07-2.el8_5   @AppStream
java-17-openjdk.x86_64                    1:17.0.1.0.12-2.el8_5     @AppStream
java-17-openjdk-devel.x86_64              1:17.0.1.0.12-2.el8_5     @AppStream
maven.noarch                              1:3.5.4-5.module_el8.0.0+39+6a9b6e22  @AppStream

若目标机器尚未安装,用下面的命令安装 JDK 17 与 Maven(实测同一命令在已安装时回显 Nothing to do,在未安装时执行真实安装事务):

sudo dnf install -y java-17-openjdk-devel maven

实测回显(输出节选,实测首尾):

Package java-17-openjdk-devel-1:17.0.1.0.12-2.el8_5.x86_64 is already installed.
Package maven-1:3.5.4-5.module_el8.0.0+39+6a9b6e22.noarch is already installed.
Dependencies resolved.
Nothing to do.
Complete!

首次安装时的真实安装事务如下(用 dnf reinstall 复现同一包的安装动作,可看到下载、安装、校验三个阶段):

sudo dnf -y reinstall java-17-openjdk-devel

实测回显(输出节选,实测首尾):

Total                                           5.1 MB/s | 5.1 MB     00:01
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
  Preparing        :                                                        1/1
  Reinstalling     : java-17-openjdk-devel-1:17.0.1.0.12-2.el8_5.x86_64     1/2
  Verifying        : java-17-openjdk-devel-1:17.0.1.0.12-2.el8_5.x86_64     2/2
Reinstalled:
  java-17-openjdk-devel-1:17.0.1.0.12-2.el8_5.x86_64
Complete!

安装完成后检查默认命令指向哪个 JDK:

readlink -f $(which java)
readlink -f $(which javac)
java -version
javac -version

实测回显:

/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/java
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.312.b07-2.el8_5.x86_64/bin/javac
openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment 21.9 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM 21.9 (build 17.0.1+12-LTS, mixed mode, sharing)
javac 1.8.0_312

可以看到 java 已经是 17,但 javac 仍指向 1.8.0_312,直接 javac 编译会产出 Java 8 字节码。这是 CentOS 8 用 alternatives 管理多 JDK 时的典型现象,需要把两者都切到 17:

sudo alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/javac 1
sudo alternatives --set javac /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/javac
sudo alternatives --set java /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/java

实测回显(--install 已注册过时无输出,--set 均无输出)。验证切换结果:

java -version
javac -version
alternatives --display java | grep "link currently"
alternatives --display javac | grep "link currently"

实测回显:

openjdk version "17.0.1" 2021-10-19 LTS
javac 17.0.1
 link currently points to /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/java
 link currently points to /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/javac

alternatives 只改 /usr/bin 下的软链,不会导出 JAVA_HOME,而 Maven 依赖 JAVA_HOME 定位编译器。写入一个开机即生效的环境变量文件:

echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64' | sudo tee /etc/profile.d/java17.sh

实测回显:

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64

重新登录(或 source /etc/profile.d/java17.sh)后验证 Maven:

mvn -v

实测回显:

Apache Maven 3.5.4 (Red Hat 3.5.4-5)
Maven home: /usr/share/maven
Java version: 17.0.1, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "6.8.0-138-generic", arch: "amd64", family: "unix"

输出中 Java version: 17.0.1 说明 Maven 已用上 JDK 17,环境准备完成。

💡 技术拓展:把 XML 映射成 Java 对象有多种做法,选型前要先分清各自的内存与用法代价。下表是常见选择的对比:

方案处理方式内存占用代码量典型场景
SAX事件流式推送,逐节点回调低高(要手写回调与状态机)超大 XML、只读遍历
DOM整棵树载入内存后操作高中需随机访问、就地修改结构
StAX拉模式流式,程序主动取事件低中需精确控制读取流程
JAXB注解绑定对象,需 XSD 生成类中低标准 XML ↔ POJO 互转
Digester规则式流式映射,边读边建对象低低配置/规则文件转对象、无固定 Schema

本文安装并使用的 Apache Commons Digester 属于「规则式流式映射」,不把整个 XML 载入内存,而是按注册的规则在解析到某个路径时创建对象、填属性、调方法,最适合处理结构松散、无需强校验的 XML 配置文件。

二、版本选型

Commons Digester 有两条并行发布线:org.apache.commons:commons-digester3(Digester 3,包名 org.apache.commons.digester3)与旧线 commons-digester:commons-digester(Digester 1/2,包名 org.apache.commons.digester)。本文使用 3.x 线。

先实测复核官方下载目录与 Maven 中央仓库的实际版本:

curl -fsSL https://downloads.apache.org/commons/digester/binaries/ | grep -oE "commons-digester3-[0-9.]+-bin.tar.gz" | sort -u
curl -fsSL https://repo1.maven.org/maven2/org/apache/commons/commons-digester3/maven-metadata.xml | grep -E "latest|release"

实测回显:

commons-digester3-3.2-bin.tar.gz
    <latest>3.2</latest>
    <release>3.2</release>

两点结论:官方下载目录当前只挂 commons-digester3-3.2 一个二进制包;Maven 中央仓库 commons-digester3 的 latest/release 同为 3.2(历史版本只有 3.0、3.1、3.2)。因此 3.x 线的最新稳定版是 3.2,并不存在坊间流传的 3.4。下表列出常用版本:

版本发布状态JDK 要求必需传递依赖适用场景
3.23.x 最新稳定版Java 5 及以上(字节码目标 1.5)cglib 2.2.2、commons-beanutils 1.8.3、commons-logging 1.1.1新项目首选
3.1稳定版Java 5 及以上同 3.23.2 有兼容顾虑时的回退
3.0稳定版Java 5 及以上同 3.2存量项目锁版本
2.1旧线最新版(commons-digester:commons-digester)Java 1.5 及以上按旧线 pom 为准老项目(Struts 1 时代)维护

关于「JDK 要求」的实测依据:解出 3.2 主 jar 的清单,其中 X-Compile-Source-JDK: 1.5、X-Compile-Target-JDK: 1.5;再用 javap 查看核心类,得到 major version: 49(Java 5 字节码)。因此 JDK 8/11/17/21 都能运行(本文用 JDK 17 实测)。

本文统一采用以下坐标与下载地址(均实测有效):

Maven 坐标:org.apache.commons:commons-digester3:3.2
发行包:https://downloads.apache.org/commons/digester/binaries/commons-digester3-3.2-bin.tar.gz
校验文件:https://downloads.apache.org/commons/digester/binaries/commons-digester3-3.2-bin.tar.gz.sha256
本地实测 sha512(本机计算):
6e27f766a6f03cada5cf505321f00c3a206721c8ca4b093364333523e625e7a1510877c75e6022301eacba9cc0f4df746f009e45f91c8847ca73d9da303097a7

两点需提前知道:Digester 3.2 没有 lib/ 目录,必需依赖(cglib、commons-beanutils、commons-logging)要自行补进类路径;官方只提供 .sha256 与 .sha1 校验文件,没有官方 .sha512。

三、方案A:Maven 中央仓库依赖

方案A通过 Maven 自动下载 jar 及其全部传递依赖,是工程化项目的推荐做法。先创建项目骨架:

mkdir -p /opt/digester-demo/src/main/java/net/quzuo/demo
mkdir -p /opt/digester-demo/src/main/resources
cd /opt/digester-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>net.quzuo.demo</groupId>
  <artifactId>digester-demo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-digester3</artifactId>
      <version>3.2</version>
    </dependency>
  </dependencies>
</project>

逐项说明:maven.compiler.source/target 指定按 Java 17 语法编译;project.build.sourceEncoding 强制源码按 UTF-8 读取;dependencies 只声明 commons-digester3 一个依赖,cglib、commons-beanutils、commons-logging 作为传递依赖自动引入。

验证依赖解析结果:

mvn dependency:tree

实测回显(输出节选,实测首尾):

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ digester-demo ---
[INFO] net.quzuo.demo:digester-demo:jar:1.0.0
[INFO] \- org.apache.commons:commons-digester3:jar:3.2:compile
[INFO]    +- cglib:cglib:jar:2.2.2:compile
[INFO]    |  \- asm:asm:jar:3.3.1:compile
[INFO]    +- commons-beanutils:commons-beanutils:jar:1.8.3:compile
[INFO]    \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

commons-digester3:3.2 直接依赖 cglib 2.2.2、commons-beanutils 1.8.3、commons-logging 1.1.1;cglib 2.2.2 又间接依赖 asm 3.3.1。四个依赖都是 compile 作用域,编译与运行都要在类路径里。

写入被映射的 POJO src/main/java/net/quzuo/demo/Server.java:

package net.quzuo.demo;

public class Server {
    private String name;
    private String host;
    private int port;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getHost() { return host; }
    public void setHost(String host) { this.host = host; }
    public int getPort() { return port; }
    public void setPort(int port) { this.port = port; }

    @Override
    public String toString() {
        return "Server{name=" + name + ", host=" + host + ", port=" + port + "}";
    }
}

写入最小验证程序 src/main/java/net/quzuo/demo/DigesterDemo.java:

package net.quzuo.demo;

import org.apache.commons.digester3.Digester;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class DigesterDemo {
    public static void main(String[] args) throws Exception {
        File xml = new File(args.length > 0 ? args[0] : "src/main/resources/servers.xml");
        Digester d = new Digester();
        d.setValidating(false);
        d.addObjectCreate("servers", ArrayList.class);
        d.addObjectCreate("servers/server", Server.class);
        d.addSetProperties("servers/server");
        d.addSetNext("servers/server", "add");
        @SuppressWarnings("unchecked")
        List<Server> servers = d.parse(xml);
        System.out.println("解析到 " + servers.size() + " 台服务器:");
        for (Server s : servers) {
            System.out.println("  " + s);
        }
        Package p = Digester.class.getPackage();
        System.out.println("commons-digester3 版本: " + p.getImplementationVersion());
    }
}

程序逻辑:addObjectCreate("servers", ArrayList.class) 在进入 <servers> 时创建 ArrayList;进入 <servers/server> 时创建 Server;addSetProperties("servers/server") 把元素属性(name/host/port)按同名 setter 灌入;addSetNext("servers/server", "add") 把刚建好的 Server 通过 add 方法挂到父对象(那个 ArrayList)上;最后从 jar 清单打印库版本。

写入输入 XML src/main/resources/servers.xml:

<?xml version="1.0" encoding="UTF-8"?>
<servers>
  <server name="web01" host="10.0.0.11" port="8080"/>
  <server name="web02" host="10.0.0.12" port="8080"/>
  <server name="db01" host="10.0.0.21" port="3306"/>
</servers>

编译并把依赖类路径导出到文件,再运行:

mvn -q compile dependency:build-classpath -Dmdep.outputFile=cp.txt
java -Dfile.encoding=UTF-8 -cp "target/classes:$(cat cp.txt)" net.quzuo.demo.DigesterDemo

实测回显:

解析到 3 台服务器:
  Server{name=web01, host=10.0.0.11, port=8080}
  Server{name=web02, host=10.0.0.12, port=8080}
  Server{name=db01, host=10.0.0.21, port=3306}
commons-digester3 版本: 3.2

三行输出分别确认了「XML → 对象列表链路」「字段赋值正确」「jar 版本为 3.2」,方案A安装完成。程序首次运行会打印 log4j:WARN No appenders could be found for logger (org.apache.commons.digester3.Digester.sax),这是 commons-logging 1.1.1 找不到日志实现时的提示,不影响功能。

四、方案B:官方发行包手动安装

方案B适合无法使用 Maven 的机器:从官方站点下载发行包,手动拼类路径编译运行。创建安装目录并下载发行包与两个官方校验文件:

sudo mkdir -p /opt/commons
cd /opt/commons
sudo curl -fsSLO https://downloads.apache.org/commons/digester/binaries/commons-digester3-3.2-bin.tar.gz
sudo curl -fsSLO https://downloads.apache.org/commons/digester/binaries/commons-digester3-3.2-bin.tar.gz.sha256
sudo curl -fsSLO https://downloads.apache.org/commons/digester/binaries/commons-digester3-3.2-bin.tar.gz.sha1
ls -l

实测回显:

total 4500
-rw-r--r-- 1 root root 2208821 Oct 13 16:47 commons-digester3-3.2-bin.tar.gz
-rw-r--r-- 1 root root      95 Oct 13 16:47 commons-digester3-3.2-bin.tar.gz.sha1
-rw-r--r-- 1 root root     119 Oct 13 16:47 commons-digester3-3.2-bin.tar.gz.sha256

先看校验文件的内容格式:

cat commons-digester3-3.2-bin.tar.gz.sha256

实测回显:

c2a6aa997f3d445ee4ef98c13122095e6ad6f55631c442a9cd79d4d792cc6183 *./digester/binaries/commons-digester3-3.2-bin.tar.gz

注意:官方校验文件是 BSD 格式(哈希 *路径),且路径与实际下载的文件名不同,直接 sha256sum -c 会因找不到文件而失败。把哈希拼成 哈希 文件名 再交给 sha256sum -c:

printf '%s  %s\n' "$(cut -d' ' -f1 commons-digester3-3.2-bin.tar.gz.sha256)" commons-digester3-3.2-bin.tar.gz | sha256sum -c -

实测回显:

commons-digester3-3.2-bin.tar.gz: OK

同一份文件再算一次 SHA-512 供交叉比对(官方未提供 .sha512):

sha512sum commons-digester3-3.2-bin.tar.gz

实测回显:

6e27f766a6f03cada5cf505321f00c3a206721c8ca4b093364333523e625e7a1510877c75e6022301eacba9cc0f4df746f009e45f91c8847ca73d9da303097a7  commons-digester3-3.2-bin.tar.gz

校验通过后解压并查看包内容:

sudo tar xzf commons-digester3-3.2-bin.tar.gz
ls -l commons-digester3-3.2/

实测回显:

total 2384
-rw-r--r-- 1  501 wheel  13307 Dec 10  2011 LICENSE.txt
-rw-r--r-- 1  501 wheel    407 Dec 10  2011 NOTICE.txt
-rw-r--r-- 1  501 wheel   2576 Dec 10  2011 RELEASE-NOTES.txt
drwxr-xr-x 4  501 wheel   4096 Dec 10  2011 apidocs
-rw-r--r-- 1  501 wheel 852037 Dec 10  2011 commons-digester3-3.2-javadoc.jar
-rw-r--r-- 1  501 wheel 282032 Dec 10  2011 commons-digester3-3.2-sources.jar
-rw-r--r-- 1  501 wheel 734212 Dec 10  2011 commons-digester3-3.2-with-deps.jar
-rw-r--r-- 1  501 wheel 241614 Dec 10  2011 commons-digester3-3.2.jar

关键点:发行包里有两种主 jar,commons-digester3-3.2.jar 是纯库 jar,commons-digester3-3.2-with-deps.jar 是把传递依赖打进同一个 jar 的「胖包」,适合单文件交付;没有 lib/ 目录,用纯库 jar 时依赖得自己补。先演示一个必踩的坑——纯库 jar 无主类,java -jar 直接失败:

java -jar commons-digester3-3.2/commons-digester3-3.2.jar

实测回显:

no main manifest attribute, in commons-digester3-3.2/commons-digester3-3.2.jar

因为它是个供别人依赖的库,清单里没有 Main-Class。正确做法是用 -cp 列出主类。逐个下载四个传递依赖 jar 到 lib/(版本以第三章 dependency:tree 实测结果为准):

cd /opt/commons/commons-digester3-3.2
sudo mkdir -p lib
cd lib
sudo curl -fsSLO https://repo1.maven.org/maven2/cglib/cglib/2.2.2/cglib-2.2.2.jar
sudo curl -fsSLO https://repo1.maven.org/maven2/asm/asm/3.3.1/asm-3.3.1.jar
sudo curl -fsSLO https://repo1.maven.org/maven2/commons-beanutils/commons-beanutils/1.8.3/commons-beanutils-1.8.3.jar
sudo curl -fsSLO https://repo1.maven.org/maven2/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar
ls -l

实测回显:

total 616
-rw-r--r-- 1 root root  43581 asm-3.3.1.jar
-rw-r--r-- 1 root root 287192 cglib-2.2.2.jar
-rw-r--r-- 1 root root 232019 commons-beanutils-1.8.3.jar
-rw-r--r-- 1 root root  60686 commons-logging-1.1.1.jar

写入与方案A相同的两个源码文件(去掉首行 package net.quzuo.demo; 放到 /opt/commons/ 下即可),这里放 /opt/commons/Server.java 与 /opt/commons/DigesterDemo.java,同时把 servers.xml 也放到 /opt/commons/。编译并运行(用 lib/* 让 JVM 自行展开所有依赖 jar):

cd /opt/commons
CP="commons-digester3-3.2/commons-digester3-3.2.jar:commons-digester3-3.2/lib/*"
javac -encoding UTF-8 -cp "$CP" -d out Server.java DigesterDemo.java
java -Dfile.encoding=UTF-8 -cp "$CP:out" DigesterDemo servers.xml

实测回显:

解析到 3 台服务器:
  Server{name=web01, host=10.0.0.11, port=8080}
  Server{name=web02, host=10.0.0.12, port=8080}
  Server{name=db01, host=10.0.0.21, port=3306}
commons-digester3 版本: 3.2

若不想逐个补依赖,也可以直接用胖包 commons-digester3-3.2-with-deps.jar,类路径只需它加 out:

cd /opt/commons
java -Dfile.encoding=UTF-8 -cp "commons-digester3-3.2/commons-digester3-3.2-with-deps.jar:out" DigesterDemo servers.xml

实测回显:

解析到 3 台服务器:
  Server{name=web01, host=10.0.0.11, port=8080}
  Server{name=web02, host=10.0.0.12, port=8080}
  Server{name=db01, host=10.0.0.21, port=3306}
commons-digester3 版本: 3.2

注意 Linux 类路径分隔符是冒号 :(Windows 是分号),lib/* 的星号由 JVM 自己展开。方案B安装完成。

五、方案对比与选型建议

对比项方案A:Maven 依赖方案B:发行包手动安装
依赖管理自动引入 3 个传递依赖与 asm手动逐个下载,漏一个就运行报错
升级成本改一行版本号重新构建重新下载、校验、解压、改脚本
离线部署需内网私服或预下载天然离线友好
可复现性pom 锁版本,强依赖脚本与人为记录
单文件交付需打包含依赖的 uber-jar直接分发 with-deps.jar
适用场景一切有构建工具的工程无 Maven 的跳板机、快速验证、交付现场

选型建议:开发/CI 环境一律方案A;目标机器没有 Maven 或需要临时排查时用方案B;方案B的四个依赖 jar 也建议从中央仓库按 dependency:tree 实测的版本号下载,避免混用其他版本(cglib 若不带上 asm,使用内省调用时会抛 NoClassDefFoundError: org/objectweb/asm/Type)。

六、多版本管理

同一台机器上共存多个 Commons Digester 版本,推荐用「版本目录 + current 符号链接」的方式管理。先准备 3.2 与 3.1 两个版本目录(3.1 的主 jar 从中央仓库按其版本下载,依赖 jar 两版共用):

cd /opt/commons
sudo mkdir -p versions/commons-digester3-3.1/lib versions/commons-digester3-3.2/lib
sudo cp commons-digester3-3.2/commons-digester3-3.2.jar versions/commons-digester3-3.2/commons-digester3-3.2.jar
sudo cp commons-digester3-3.2/lib/*.jar versions/commons-digester3-3.2/lib/
sudo curl -fsSL -o versions/commons-digester3-3.1/commons-digester3-3.1.jar https://repo1.maven.org/maven2/org/apache/commons/commons-digester3/3.1/commons-digester3-3.1.jar
sudo cp commons-digester3-3.2/lib/*.jar versions/commons-digester3-3.1/lib/
sudo ln -sfn versions/commons-digester3-3.2 current
ls -l current

实测回显:

lrwxrwxrwx 1 root root 30 Oct 13 16:54 current -> versions/commons-digester3-3.2

写一个只打印库版本的探针 VersionProbe.java,编译一次即可反复用:

import org.apache.commons.digester3.Digester;

public class VersionProbe {
    public static void main(String[] args) {
        System.out.println("commons-digester3 版本: "
            + Digester.class.getPackage().getImplementationVersion());
    }
}

编译(类路径含当前版本的 digester jar 与其依赖,探针代码两版通用):

cd /opt/commons
mkdir -p outv
javac -encoding UTF-8 -cp "current/commons-digester3-3.2.jar:current/lib/*" -d outv VersionProbe.java

该组命令无输出。用 current 链接运行,确认当前指向 3.2:

java -Dfile.encoding=UTF-8 -cp "current/commons-digester3-3.2.jar:current/lib/*:outv" VersionProbe

实测回显:

commons-digester3 版本: 3.2

切换链接到 3.1 后再运行(类路径里的版本 jar 同步换成 3.1):

sudo ln -sfn versions/commons-digester3-3.1 current
ls -l current
java -Dfile.encoding=UTF-8 -cp "current/commons-digester3-3.1.jar:current/lib/*:outv" VersionProbe

实测回显:

lrwxrwxrwx 1 root root 30 Oct 13 16:54 current -> versions/commons-digester3-3.1
commons-digester3 版本: 3.1

程序打印的版本号随符号链接同步变化,证明切换生效。切回最新版:

sudo ln -sfn versions/commons-digester3-3.2 current

该命令无输出。关键约束:版本 jar 路径要写成 current/commons-digester3-<版本>.jar 或让脚本按链接解析,任何一处写死成版本号目录,切换都会失效。

七、卸载

卸载分三部分:手动安装目录、Maven 本地仓库缓存、以及不再需要时的 JDK/Maven 本身。清理手动安装目录与验证产物:

sudo rm -rf /opt/commons
sudo rm -rf /opt/digester-demo

该组命令无输出。Maven 本地仓库中 digester 及其传递依赖缓存一并清理:

du -sh ~/.m2/repository/org/apache/commons/commons-digester3 ~/.m2/repository/cglib ~/.m2/repository/asm ~/.m2/repository/commons-beanutils ~/.m2/repository/commons-logging
rm -rf ~/.m2/repository/org/apache/commons/commons-digester3 ~/.m2/repository/cglib ~/.m2/repository/asm ~/.m2/repository/commons-beanutils ~/.m2/repository/commons-logging

实测回显(第一条命令,实测值):

276K    /root/.m2/repository/org/apache/commons/commons-digester3
308K    /root/.m2/repository/cglib
68K     /root/.m2/repository/asm
484K    /root/.m2/repository/commons-beanutils
196K    /root/.m2/repository/commons-logging

若 JDK 17 与 Maven 也不再有他用,可一并卸载(CentOS 8 用 dnf):

sudo dnf remove -y java-17-openjdk-devel maven

实测回显(输出节选,实测首尾):

Running transaction
  Preparing        :                                                        1/1
  Erasing          : maven-1:3.5.4-5.module_el8.0.0+39+6a9b6e22.noarc     1/135
  Erasing          : maven-lib-1:3.5.4-5.module_el8.0.0+39+6a9b6e22.n     2/135
  Erasing          : maven-wagon-http-3.1.0-1.module_el8.0.0+39+6a9b6     3/135
  Erasing          : java-1.8.0-openjdk-devel-1:1.8.0.312.b07-2.el8_5    14/135
Complete!

随后校验:

java -version
mvn -v

实测回显:

bash: java: command not found
bash: mvn: command not found

java/mvn 命令消失即卸载完成。两点实测注意:一是 CentOS 8 的 maven 包把 java-1.8.0-openjdk-devel 作为依赖,卸载 Maven 会连带移除它(输出中第 14 项即为该包);二是卸载过程可能出现 warning: %postun(...) scriptlet failed 与 Failed to set locale 提示,属该模块包的已知现象,不影响卸载结果。若之前用 alternatives 切过 javac,卸载后该 alternative 会失效,需要时可用 alternatives --remove javac /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/javac 清理残留项。

八、常见问题

Q1:CentOS 8 上装完 JDK 17,为什么 java 用 17 而 javac 还是 1.8? CentOS 8 用 alternatives 机制管理多 JDK,java 与 javac 是两个独立的 alternative 组,切换 java 不会联动 javac。实测 readlink -f $(which javac) 仍指向 java-1.8.0-openjdk-...,javac -version 输出 1.8.0_312。解决:分别对两个组执行 alternatives --install 注册 JDK 17 的 javac、再 alternatives --set javac /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/javac(见第一章命令),切换后 javac -version 输出 17.0.1。

Q2:Maven 编译时找不到可用 JDK 或仍用旧版本怎么办? alternatives 只改 /usr/bin 软链,不导出 JAVA_HOME,而 Maven 依赖 JAVA_HOME。实测未设置时 echo $JAVA_HOME 为空。解决:写入 /etc/profile.d/java17.sh 导出 JAVA_HOME,重新登录后用 mvn -v 检查,其 Java version: 行应为 17.0.1。

Q3:java -jar commons-digester3-3.2.jar 报 no main manifest attribute 怎么办? 这是设计如此:纯库 jar 只供其他项目依赖,清单里没有 Main-Class。要么改用 java -cp "主jar:lib/*" 你的主类,要么直接用胖包 commons-digester3-3.2-with-deps.jar 并带上你的类目录。

Q4:运行时报 NoClassDefFoundError: org/objectweb/asm/Type 或 beanutils 包下的类找不到,怎么处理? 说明传递依赖没进类路径。3.2 的必需依赖是 cglib 2.2.2(其又依赖 asm 3.3.1)、commons-beanutils 1.8.3、commons-logging 1.1.1,Maven 项目会自动带入;手动部署时必须把四个 jar 全部放进 lib/,或直接用 with-deps.jar。

Q5:为什么 3.x 线最新版是 3.2 而不是 3.4? 实测 Maven 中央仓库 commons-digester3 的 maven-metadata.xml 中 <latest>/<release> 均为 3.2,可用版本只有 3.0、3.1、3.2;官方下载目录也只挂 3.2。3.2 发布于 2011 年后长期未再发新版,所谓「3.4」并不存在,选型请以实测目录页为准。

九、参考链接

  1. Apache Commons Digester 官方主页:https://commons.apache.org/proper/commons-digester/
  2. 3.2 发行包下载:https://downloads.apache.org/commons/digester/binaries/commons-digester3-3.2-bin.tar.gz
  3. 发行包 sha256 校验文件:https://downloads.apache.org/commons/digester/binaries/commons-digester3-3.2-bin.tar.gz.sha256
  4. Maven 中央仓库 commons-digester3 目录(含 pom 与各分类 jar):https://repo1.maven.org/maven2/org/apache/commons/commons-digester3/3.2/
  5. Apache 归档下载站(历史版本):https://archive.apache.org/dist/commons/digester/binaries/
  6. 官方 javadoc(3.2 在线 API 文档):https://commons.apache.org/proper/commons-digester/apidocs/index.html
  7. CentOS 8 JDK 17 包说明:https://vault.centos.org/8.5.2111/AppStream/x86_64/os/Packages/
  8. Maven 安装文档:https://maven.apache.org/install.html