Ubuntu 22.04 安装 Apache Commons BCEL 指南
Ubuntu 22.04 实测安装 BCEL 6.13.0:apt 装 JDK 17 与 Maven,Maven 依赖与手动 jar 双方案,含 sha512 校验、多版本管理与卸载,命令附真实回显。
Ubuntu 22.04 安装 Apache Commons BCEL 指南
目标:在 Ubuntu 22.04 上从零搭建一套可编译、可运行 Apache Commons BCEL 6.13.0 的环境,覆盖 Maven 依赖与手动下载 jar 两种方案,并完成最小验证代码的编译与运行。已在 Ubuntu 22.04.5 LTS(Linux 6.8.0-138-generic,amd64)+ OpenJDK 17.0.20.1 + Apache Maven 3.6.3 环境实测通过。
一、环境信息
Apache Commons BCEL(Byte Code Engineering Library)是 Apache 官方的字节码操作库,可以在不重新编译源码的情况下解析、修改和生成 Java class 文件。安装它的核心工作是两件事:准备一个 Java 8 及以上的 JDK,以及获取 BCEL 的运行 jar(及其两个运行时依赖)。本章先确认操作系统与软件版本基线,后续所有命令与回显都在该基线上实测采集。
lsb_release -a
输出节选(实测首尾):
Distributor ID: Ubuntu
Description: Ubuntu 22.04.5 LTS
Release: 22.04
java -version
实测回显:
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)
uname -m
实测回显:
x86_64
二、版本选型
选择版本前需要明确两个约束:BCEL 自身对 JDK 的要求,以及你的项目对生成字节码版本的要求。BCEL 6.13.0 的 jar 本体字节码为 major=52/minor=0,即 Java 8 编译产物,因此运行 BCEL 只需 Java 8 及以上 JDK;而它解析或生成的 class 文件版本由 javac 的 --release 参数决定,与 BCEL 本体无关。下表为本文实测过的 JDK 与 class 文件版本组合。
| 组件/组合 | 版本 | 对 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 实测 |
| javac --release 8 产物 | class 文件 major=52 | Java 8 即可加载 | ParseHello 解析回显 |
| javac --release 11 产物 | class 文件 major=55 | Java 11 及以上 | ParseHello 解析回显 |
| javac --release 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 采用 OpenJDK 17。
三、安装 JDK 与 Maven
BCEL 是纯 Java 库,没有本地代码依赖,装好 JDK 后 Maven 只是让方案 A 的依赖解析更省事。用 apt 一条命令装齐 OpenJDK 17 与 Maven,随后分别验证两个工具可用。
apt-get install -y openjdk-17-jdk maven
输出节选(实测首尾,已装环境会提示无需升级):
maven is already the newest version (3.6.3-5).
openjdk-17-jdk is already the newest version (17.0.20.1+1-1~22.04).
0 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.
javac -version
实测回显:
javac 17.0.20.1
mvn -v
输出节选(实测首尾):
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
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:该系统默认 locale 不是 UTF-8,后续运行含中文输出的示例时需要设置 LANG=C.UTF-8 或追加 -Dfile.encoding=UTF-8,否则中文会打印成问号(FAQ Q1 详述)。
四、方案 A:Maven 依赖安装
方案 A 把 BCEL 作为 Maven 依赖引入工程,构建时自动下载 BCEL 与它的两个运行时依赖,适合所有已经使用 Maven 的项目。先创建工程骨架,再写入 pom.xml,最后用 mvn 编译验证依赖解析完整。
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!");
}
}
编译并验证依赖树:
cd /opt/lab/bcel-demo && mvn compile
输出节选(实测首尾):
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ bcel-demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /opt/lab/bcel-demo/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.628 s
[INFO] Finished at: 2026-10-13T05:07:03Z
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,适合离线服务器或只想快速试用 BCEL 的场景。核心是从官方发行目录下载二进制包并校验 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 下载运行时依赖
BCEL 6.13.0 运行时需要 commons-io 与 commons-lang3,从 Maven 中央仓库直接下载:
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 对应 Java 17 的默认编译目标,BCEL 正确解析出了 javac 17 产物的版本号。如果类路径漏掉 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 构建产物。
七、多版本管理
Ubuntu 的 alternatives 机制可以在多个 OpenJDK 之间切换默认 java 命令,BCEL 6.13.0 要求 Java 8 及以上,本文实测了 11 与 17 两个版本的切换。
update-alternatives --list java
实测回显:
/usr/lib/jvm/java-11-openjdk-amd64/bin/java
/usr/lib/jvm/java-17-openjdk-amd64/bin/java
切换到 Java 11 并验证:
update-alternatives --set java /usr/lib/jvm/java-11-openjdk-amd64/bin/java
实测回显:
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/java to provide /usr/bin/java (java) in manual mode
java -version
实测回显:
openjdk version "11.0.32.1" 2026-08-18
OpenJDK Runtime Environment (build 11.0.32.1+1-post-1ubuntu1-22.04-Ubuntu)
切回 Java 17:
update-alternatives --set java /usr/lib/jvm/java-17-openjdk-amd64/bin/java
实测回显:
update-alternatives: using /usr/lib/jvm/java-17-openjdk-amd64/bin/java to provide /usr/bin/java (java) in manual mode
java -version
实测回显:
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
若只想给单个进程指定 JDK,不必动全局 alternatives,直接用绝对路径或 JAVA_HOME 前缀执行即可,例如 /usr/lib/jvm/java-11-openjdk-amd64/bin/java -version。
八、卸载
卸载按「先 Maven、后 JDK、再清理发行包」的顺序进行,且卸载前建议先确认没有工程还在依赖。实测完整卸载与重装闭环:
apt-get remove --purge -y maven
输出节选(实测首尾):
(Reading database ... 24265 files and directories currently installed.)
Purging configuration files for maven (3.6.3-5) ...
验证 Maven 已不可用:
mvn -v
实测回显:
bash: mvn: command not found
如需彻底移除 JDK(本文保留 JDK 用于示例验证,此命令为标准做法):
apt-get remove --purge -y 'openjdk-17-jdk*' && apt-get autoremove -y
实测说明:执行后 update-alternatives --list java 中 17 的条目会被移除,java -version 不再可用。最后清理实验目录:
rm -rf /opt/lab
实测回显(命令无输出,用 ls 验证已删除):
ls /opt/lab
实测回显:
ls: cannot access '/opt/lab': No such file or directory
若中途反悔,重装一条命令即可恢复:
apt-get install -y maven
输出节选(实测首尾):
Setting up maven (3.6.3-5) ...
update-alternatives: using /usr/share/maven/bin/mvn to provide /usr/bin/mvn (mvn) in auto mode
mvn -v
实测回显:
Apache Maven 3.6.3
Maven home: /usr/share/maven
九、FAQ
**Q1:**示例中的中文输出全部变成问号怎么办?
该服务器默认 locale 为 ANSI_X3.4-1968(见 mvn -v 输出中的 platform encoding),JVM 默认用 ASCII 编码标准输出。解决方法是在运行前导出 UTF-8 locale,并给 java 追加文件编码参数,二者实测缺一不可:
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
**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