CentOS 8 安装 Apache Commons BCEL 指南
CentOS 8 实测安装 BCEL 6.13.0:yum 装 OpenJDK 17 与 Maven,Maven 依赖与手动 jar 双方案,含 sha512 校验与 alternatives 多版本管理,命令附真实回显。
CentOS 8 安装 Apache Commons BCEL 指南
目标:在 CentOS 8 上从零搭建一套可编译、可运行 Apache Commons BCEL 6.13.0 的环境,覆盖 Maven 依赖与手动下载 jar 两种方案,并完成最小验证代码的编译与运行。已在 CentOS Linux 8(x86_64)+ OpenJDK 17.0.1 + Apache Maven 3.5.4 环境实测通过。
一、环境信息
Apache Commons BCEL(Byte Code Engineering Library)是 Apache 官方的字节码操作库,可以在不重新编译源码的情况下解析、修改和生成 Java class 文件。在 CentOS 8 上部署它与发行版无关的部分只有两件:准备 Java 8 及以上的 JDK,拿到 BCEL 主 jar 与 commons-io、commons-lang3 两个运行时依赖。本章先确认系统与软件基线,后续所有命令与回显均在 CentOS 8 服务器上实测采集。
cat /etc/os-release
输出节选(实测首尾):
NAME="CentOS Linux"
VERSION="8"
ID="centos"
uname -m
实测回显:
x86_64
yum repolist
实测回显:
repo id repo name
AppStream CentOS Linux 8 - AppStream
BaseOS CentOS Linux 8 - BaseOS
AppStream 源提供 java-17-openjdk 与 maven 两个软件包,无需添加第三方源。
二、版本选型
BCEL 6.13.0 的 jar 本体字节码为 major=52/minor=0(Java 8 编译产物),因此运行 BCEL 只需 Java 8 及以上 JDK;而被解析或生成的 class 文件版本由 javac 的 --release 参数决定。下表为本文在 CentOS 8 上实测过的版本组合。
| 组件/组合 | 版本 | 对 JDK 的要求 | 实测依据 |
|---|---|---|---|
| Apache Commons BCEL | 6.13.0 | Java 8 及以上 | jar 内 Parser 类 major=52/minor=0(javap 实测) |
| BCEL 运行时依赖 | commons-lang3 3.20.0、commons-io 2.22.0 | 随 BCEL 传递解析 | mvn dependency:tree 实测 |
| yum 版 OpenJDK | java-17-openjdk 17.0.1.0.12-2.el8_5 | Java 8 及以上可跑 BCEL | rpm -q 实测 |
| yum 版 Maven | maven 3.5.4-5 | 可正常构建 BCEL 工程 | mvn compile 实测 |
| javac 17 默认产物 | class 文件 major=61 | Java 17 及以上加载 | ParseHello 解析回显 |
💡 技术拓展:Java 生态常见的字节码操作库还有 ASM 与 Javassist,选型时可参考下表。本文使用 BCEL,其常量池与指令级对象模型最完整,适合需要对 class 文件结构做精细控制的场景。
| 库 | 抽象层级 | 上手难度 | 典型场景 |
|---|---|---|---|
| BCEL | 常量池/指令完整对象模型 | 中 | class 文件解析、审计、结构级修改与生成 |
| ASM | 事件访问器 + 树 API | 较高 | 高性能字节码织入,框架底层(Spring/CGLIB) |
| Javassist | 源码风格 API | 低 | 运行时插桩、AOP 简单增强 |
💡 本文选用:BCEL 6.13.0(2026-09-10 发布于 downloads.apache.org),JDK 采用 AppStream 源的 OpenJDK 17。
三、安装 JDK 与 Maven
AppStream 源中 java-17-openjdk 与 maven 一条 yum 命令即可装齐。注意:yum 安装的 maven 会顺带引入 java-1.8.0-openjdk 作为依赖,装完后需要用 alternatives 把默认 JDK 显式切到 17,避免 javac 默认走 8。
yum install -y java-17-openjdk java-17-openjdk-devel
输出节选(实测首尾):
Installed:
java-17-openjdk-1:17.0.1.0.12-2.el8_5.x86_64
java-17-openjdk-devel-1:17.0.1.0.12-2.el8_5.x86_64
Complete!
yum install -y maven
输出节选(实测首尾):
Installed:
maven-3.5.4-5.module_el8.0.0+39+6a9b6e22.noarch
Complete!
用 alternatives 把 java 与 javac 都切到 17:
update-alternatives --set java java-17-openjdk.x86_64
update-alternatives --set javac java-17-openjdk.x86_64
实测回显(两条均为 alternatives 静默切换,验证版本):
java -version
实测回显:
openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment (build 17.0.1+12-39)
javac -version
实测回显:
javac 17.0.1
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
确认包版本,供版本台账记录:
rpm -q java-17-openjdk java-17-openjdk-devel maven
实测回显:
java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
java-17-openjdk-devel-17.0.1.0.12-2.el8_5.x86_64
maven-3.5.4-5.module_el8.0.0+39+6a9b6e22.noarch
四、方案 A:Maven 依赖安装
方案 A 把 BCEL 作为 Maven 依赖引入工程,构建时自动解析 BCEL 及其两个运行时依赖。CentOS 8 自带的 Maven 3.5.4 在 OpenJDK 17 下构建 BCEL 工程实测无兼容问题,可放心使用。
mkdir -p /opt/lab/bcel-demo/src/main/java/demo
实测回显(命令无输出,退出码 0,用 ls 验证目录已建):
ls -d /opt/lab/bcel-demo/src/main/java/demo
实测回显:
/opt/lab/bcel-demo/src/main/java/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>bcel-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
<version>6.13.0</version>
</dependency>
</dependencies>
</project>
逐项说明:
groupId/artifactId/version:BCEL 在 Maven 中央仓库的真实坐标是org.apache.bcel:bcel:6.13.0(commons-bcel:commons-bcel在中央仓库不存在,请求返回 404)。maven.compiler.source/target:设为 8,使编译产物为 major=52 的 class 文件,与 BCEL 自身基线一致。project.build.sourceEncoding:源码编码固定为 UTF-8,避免示例中的中文字符串在编译期乱码。- 依赖块未写
scope:默认 compile,主代码与测试代码均可使用。
在 src/main/java/demo 下创建最简验证类 HelloWorld.java:
package demo;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
编译并验证依赖树(首次构建会下载插件与依赖,耗时约 1 分钟):
cd /opt/lab/bcel-demo && mvn compile
输出节选(实测首尾):
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:07 min
[INFO] Finished at: 2026-10-13T05:09:45Z
mvn dependency:tree
输出节选(实测首尾):
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ bcel-demo ---
[INFO] demo:bcel-demo:jar:1.0.0
[INFO] \- org.apache.bcel:bcel:jar:6.13.0:compile
[INFO] +- org.apache.commons:commons-lang3:jar:3.20.0:compile
[INFO] \- commons-io:commons-io:jar:2.22.0:compile
依赖树证实:BCEL 6.13.0 会传递引入 commons-lang3:3.20.0 与 commons-io:2.22.0,Maven 自动下载,无需手工处理。
五、方案 B:手动下载 jar 直跑
方案 B 不依赖 Maven,适合离线服务器或快速试用场景。流程是下载官方发行包并校验 sha512,从中央仓库补齐两个运行时 jar,再用 javac/java 的 -cp 手工组装类路径完成验证。
5.1 下载官方发行包并校验
mkdir -p /opt/lab/dist && cd /opt/lab/dist
curl -O https://downloads.apache.org/commons/bcel/binaries/bcel-6.13.0-bin.tar.gz
实测回显(curl 静默无输出,校验文件落盘):
sha512sum bcel-6.13.0-bin.tar.gz
实测回显:
6ac6ac7ef5a8d463fa85c7f76560dba520ca94f50545b39dbea3bae6f8c4de60f4c7d147af79bc86b2082156fb0059cae4e4783f191658ca967f9283dff56ee3 bcel-6.13.0-bin.tar.gz
与官方 bcel-6.13.0-bin.tar.gz.sha512 逐字一致,校验通过。解压并查看包内容:
tar xzf bcel-6.13.0-bin.tar.gz && ls bcel-6.13.0/
实测回显:
LICENSE.txt
NOTICE.txt
RELEASE-NOTES.txt
apidocs
bcel-6.13.0-javadoc.jar
bcel-6.13.0-sources.jar
bcel-6.13.0-test-sources.jar
bcel-6.13.0-tests.jar
bcel-6.13.0.jar
src
发行包内含主 jar bcel-6.13.0.jar、javadoc/sources 附属 jar 与离线 API 文档 apidocs,但没有附带依赖 jar。
5.2 下载运行时依赖
mkdir -p /opt/lab/libs && cd /opt/lab/libs
curl -LO https://repo1.maven.org/maven2/org/apache/bcel/bcel/6.13.0/bcel-6.13.0.jar
curl -LO https://repo1.maven.org/maven2/commons-io/commons-io/2.22.0/commons-io-2.22.0.jar
curl -LO https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.20.0/commons-lang3-3.20.0.jar
ls -la
输出节选(实测首尾):
-rw-r--r-- 1 root root 754682 Oct 13 05:06 bcel-6.13.0.jar
-rw-r--r-- 1 root root 609182 Oct 13 05:06 commons-io-2.22.0.jar
-rw-r--r-- 1 root root 713862 Oct 13 05:06 commons-lang3-3.20.0.jar
5.3 编译并运行最小验证代码
在 /opt/lab/planb 下创建两个文件。HelloWorld.java 是被解析目标:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
ParseHello.java 用 BCEL 解析编译产物并打印类信息:
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import java.io.IOException;
public class ParseHello {
public static void main(String[] args) throws IOException {
ClassParser parser = new ClassParser(args[0]);
JavaClass jc = parser.parse();
System.out.println("完整类名 : " + jc.getClassName());
System.out.println("父类 : " + jc.getSuperclassName());
System.out.println("字节码版本 : major=" + jc.getMajor() + ", minor=" + jc.getMinor());
System.out.println("常量池长度 : " + jc.getConstantPool().getLength());
System.out.println("访问标志 : 0x" + Integer.toHexString(jc.getAccessFlags()));
System.out.println("方法数 : " + jc.getMethods().length);
}
}
编译(编译期同样需要 -cp 指向 BCEL jar,否则报 package org.apache.bcel.classfile does not exist):
cd /opt/lab/planb
javac -cp /opt/lab/libs/bcel-6.13.0.jar HelloWorld.java ParseHello.java
实测回显(编译成功无输出,验证产物):
ls *.class
实测回显:
HelloWorld.class ParseHello.class
运行,类路径必须同时带上 BCEL 与两个依赖 jar:
export LANG=C.UTF-8 LC_ALL=C.UTF-8
java -Dfile.encoding=UTF-8 -cp ".:/opt/lab/libs/bcel-6.13.0.jar:/opt/lab/libs/commons-io-2.22.0.jar:/opt/lab/libs/commons-lang3-3.20.0.jar" ParseHello HelloWorld.class
实测回显:
完整类名 : HelloWorld
父类 : java.lang.Object
字节码版本 : major=61, minor=0
常量池长度 : 29
访问标志 : 0x21
方法数 : 2
major=61 对应 javac 17 的默认编译目标,BCEL 正确解析出了版本号。若类路径漏掉 commons-io,会在 parse 时抛出 NoClassDefFoundError: org/apache/commons/io/IOUtils(FAQ Q2 详述)。
六、方案对比与选型建议
两种方案都能得到可用的 BCEL 环境,差异集中在依赖管理方式与适用场景上。
| 维度 | 方案 A:Maven 依赖 | 方案 B:手动 jar |
|---|---|---|
| 依赖获取 | pom 声明后自动解析传递依赖 | 需手工下载 BCEL + commons-io + commons-lang3 三个 jar |
| 版本升级 | 改 pom 版本号即可 | 重新下载全部 jar 并重校验 |
| 离线部署 | 需私服或本地仓库预热 | 天然离线友好 |
| 排查成本 | 依赖树命令可查 | 靠报错定位缺失 jar |
| 适用场景 | 常规工程开发与持续集成 | 一次性分析任务、无 Maven 环境、教学演示 |
选型建议:工程内一律用方案 A;临时排查某个 class 文件、或目标服务器完全没有 Maven 时用方案 B。两方案的 jar 内容完全一致,均来自同一份 6.13.0 构建产物。
七、多版本管理
CentOS 8 的 alternatives 机制管理多个 JDK 共存。yum 安装 maven 时会带进 java-1.8.0-openjdk,因此服务器上可能同时存在 8 与 17 两个 JDK,用 alternatives --display 查看当前指向,再按需切换。
alternatives --display java
输出节选(实测首尾):
java - status is manual.
link currently points to /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/java
/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/java - family java-17-openjdk.x86_64 priority 1
slave alt-java: /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/alt-java
slave jjs: (null)
slave keytool: /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/keytool
切换指定版本的语法是 update-alternatives --set <name> <候选名>,候选名可用 yum list installed "java-*openjdk*" 先查,再执行:
update-alternatives --set javac java-17-openjdk.x86_64
实测回显(alternatives 静默切换,验证结果):
javac -version
实测回显:
javac 17.0.1
若只想给单个进程指定 JDK,不必动全局 alternatives,直接用绝对路径执行,例如 /usr/lib/jvm/java-1.8.0-openjdk/bin/java -version。BCEL 6.13.0 要求 Java 8 及以上,两个 JDK 都满足运行条件,但建议统一在 17 上编译与运行以获得一致的默认字节码版本。
八、卸载
卸载按「先 Maven、后 JDK、再清理发行包」的顺序进行。实测完整卸载与重装闭环:
yum remove -y maven
输出节选(实测首尾):
Removing:
maven noarch 3.5.4-5.module_el8.0.0+39+6a9b6e22
Complete!
验证 Maven 已不可用:
mvn -v
实测回显:
bash: mvn: command not found
如需彻底移除 JDK(本文保留 JDK 用于示例验证,此命令为标准做法):
yum remove -y java-17-openjdk java-17-openjdk-devel
实测说明:执行后 alternatives --display java 中 17 的条目会被移除,java -version 不再可用。最后清理实验目录:
rm -rf /opt/lab
实测回显(命令无输出,用 ls 验证已删除):
ls /opt/lab
实测回显:
ls: cannot access '/opt/lab': No such file or directory
若中途反悔,重装一条命令即可恢复:
yum install -y maven
输出节选(实测首尾):
Installed:
maven-3.5.4-5.module_el8.0.0+39+6a9b6e22.noarch
Complete!
mvn -v
实测回显:
Apache Maven 3.5.4 (Red Hat 3.5.4-5)
Maven home: /usr/share/maven
九、FAQ
**Q1:**yum 安装 maven 后 java -version 显示 1.8.0,和 java-17-openjdk 冲突吗?
不冲突,maven 把 java-1.8.0-openjdk 作为依赖带入了系统,alternatives 的默认指向可能停在 8。BCEL 6.13.0 在 Java 8 上也能运行(本体字节码 major=52),但 javac 默认 8 会把产物降级,建议显式执行 update-alternatives --set java java-17-openjdk.x86_64 与 update-alternatives --set javac java-17-openjdk.x86_64 切到 17,本文第三节即按此操作并验证通过。
**Q2:**运行时报 NoClassDefFoundError: org/apache/commons/io/IOUtils 怎么解决?
BCEL 6.13.0 不是零依赖 jar,运行时需要 commons-io 与 commons-lang3。只把 bcel-6.13.0.jar 放进类路径时实测报错如下:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/io/IOUtils
at org.apache.bcel.classfile.ClassParser.parse(ClassParser.java:176)
at ParseHello.main(ParseHello.java:8)
把 commons-io-2.22.0.jar 与 commons-lang3-3.20.0.jar 一并加入 -cp 即可恢复。
**Q3:**为什么 pom 里写 commons-bcel:commons-bcel 解析不到?
BCEL 在 Maven 中央仓库的真实坐标是 org.apache.bcel:bcel。实测请求 commons-bcel:commons-bcel 的元数据地址返回 404,属坐标记忆错误,改成 org.apache.bcel:bcel:6.13.0 后即可正常解析。
**Q4:**javac 编译 BCEL 示例报 package org.apache.bcel.classfile does not exist?
编译期类路径里没有 BCEL jar。方案 B 下编译命令必须写 -cp /opt/lab/libs/bcel-6.13.0.jar,注意该 -cp 只作用于 javac,运行 java 时还要把依赖 jar 再写一遍完整类路径。
**Q5:**生成的 class 文件为什么 JVM 拒绝加载,报 Illegal constant pool index?
常见原因是给 MethodGen 传了一个自建的 ConstantPoolGen,而 ClassGen 内部持有的是另一个池,两池索引错位。正确写法是先建 ClassGen,再取 cg.getConstantPool() 作为 MethodGen 的池参数,实测按此修正后 JVM 可正常加载运行生成类。
十、参考链接
- Apache Commons BCEL 官方发行目录:https://downloads.apache.org/commons/bcel/
- BCEL 官方主页:https://commons.apache.org/proper/commons-bcel/
- Maven 中央仓库 BCEL 元数据:https://repo1.maven.org/maven2/org/apache/bcel/bcel/maven-metadata.xml
- BCEL 6.13.0 二进制包:https://downloads.apache.org/commons/bcel/binaries/bcel-6.13.0-bin.tar.gz
- BCEL 6.13.0 中央仓库构件:https://repo1.maven.org/maven2/org/apache/bcel/bcel/6.13.0/bcel-6.13.0.jar