Ubuntu 22.04 安装 Apache Commons Weaver 指南
在 Ubuntu 22.04 上安装 Apache Commons Weaver 2.0(JDK 8 构建期织入器),覆盖 apt 安装 JDK 8 与 Maven、Maven 插件依赖与官方 bin 包两种方案、privilizer 织入最小验证、JDK 17 兼容性实测结论与卸载清理,命令回显均实测通过。
Ubuntu 22.04 安装 Apache Commons Weaver 指南
目标:在 Ubuntu 22.04 上从零搭建 Apache Commons Weaver 2.0 的使用环境(JDK 8 + Maven 3.6.3),跑通 privilizer 织入提供者的最小验证工程,并给出官方 bin 包 + 手动 classpath 的离线方案;本文全部命令与回显均已在 Ubuntu 22.04 实测通过。
Apache Commons Weaver 是 Apache Commons 下的构建期字节码织入框架:它不是一个业务工具库,而是一套「扫描编译产物 class → 按注解改写字节码 → 写回」的处理器体系。官方内置两个织入提供者:privilizer(把标注 @Privileged 的方法体重写为 AccessController.doPrivileged 特权块)与 normalizer(把重复生成的匿名类收敛到规范位置)。它的「安装」本质是:装好 JDK 与构建工具 → 引入 processor/织入提供者模块 → 用 Maven 插件或命令行触发织入 → 用 javap 验证产物。特别地,Weaver 2.0 的织入器只能在 JDK 8 上运行(实测 JDK 17 直接报错,见第三章与 FAQ),本文按 JDK 8 编写全流程。
一、环境信息
本节先固定可复现的基线环境,后续所有命令均在此环境执行。
- 操作系统:Ubuntu 22.04.5 LTS(
PRETTY_NAME="Ubuntu 22.04.5 LTS") - 架构:x86_64
- JDK:OpenJDK 1.8.0_504(apt 包
openjdk-8-jdk,织入器实测必须运行在 JDK 8) - 构建工具:Apache Maven 3.6.3(apt 包
maven) - 组件版本:Apache Commons Weaver 2.0(2018-09-07 发布)
- Maven 坐标:
org.apache.commons:commons-weaver-processor:2.0、org.apache.commons:commons-weaver-privilizer:2.0、org.apache.commons:commons-weaver-privilizer-api:2.0、org.apache.commons:commons-weaver-maven-plugin:2.0 - 官方下载目录:
https://downloads.apache.org/commons/weaver/ - 验证用工作目录:
/work/mini-demo(Maven 最小验证工程)、/work/planb(bin 包直跑)
系统与架构确认(实时回显):
cat /etc/os-release | head -3
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
uname -m
x86_64
💡 技术拓展:Java 生态里「给已有 class 增加能力」有三条主流路线,Weaver 属于构建期织入——
路线 代表 发生时机 特点 构建期字节码织入 Commons Weaver、AspectJ ajc javac编译之后、打包之前产物自包含,运行期零额外依赖 类加载期织入(LTW) AspectJ Weaver(-javaagent) JVM 加载 class 时 不改构建流程,但需 agent 与 weaving classloader 运行期动态代理 JDK Proxy / CGLIB 程序运行时 只能包装接口/可继承方法,无字节码改动 本文采用 Commons Weaver(构建期织入):织入在
mvn package阶段完成,运行期 classpath 干净(实测织入产物运行无需携带任何 Weaver jar)。
二、前置条件
本节说明安装前必须满足的条件。
- 具有 root 或 sudo 权限的 Ubuntu 22.04 系统。
- 可访问 Ubuntu 官方软件源(
apt update能正常拉取索引)。 - 可访问 Maven 中央仓库
repo1.maven.org与 Apache 官方下载站downloads.apache.org(本文实测网络直连可达)。 - 至少 1 GB 可用磁盘空间(JDK 8 + Maven + 织入模块依赖合计约 400 MB)。
网络连通性确认(实时回显):
curl -sI --max-time 20 https://repo1.maven.org/maven2/org/apache/commons/commons-weaver-processor/2.0/ | head -1
HTTP/1.1 200 OK
curl -sI --max-time 20 https://downloads.apache.org/commons/weaver/ | head -1
HTTP/1.1 200 OK
三、版本选型说明
本节回答「装哪个版本、用哪个 JDK」。Commons Weaver 是典型的低频发布项目:官方下载目录 https://downloads.apache.org/commons/weaver/ 上只有 2.0 一个版本线,binaries/ 目录实测内容如下:
curl -s --max-time 30 https://downloads.apache.org/commons/weaver/binaries/ | grep -oE 'commons-weaver[^"<> ]*\.tar\.gz|commons-weaver[^"<> ]*\.zip' | sort -u
commons-weaver-2.0-bin.tar.gz
commons-weaver-2.0-bin.zip
RELEASE-NOTES.txt(2018-09-07)明确写着 "Requires Java 8 or greater",本版核心变更为升级 Java 8 基线(WEAVER-19)、xbean-finder 4.9(WEAVER-21)、ASM 6.2.1(WEAVER-22)、commons-parent v47(WEAVER-26)。2.0 之后官方未再发布新版本,维护处于停滞状态;但它的模块在 Maven 中央仓库全部可用且 API 稳定,生产使用需自行评估「低频维护」这一事实。
| 模块(中央仓库实测存在) | 作用 | 是否必须 |
|---|---|---|
commons-weaver-processor:2.0 | 织入处理器内核:扫描 class、按 SPI 加载提供者 | 必须 |
commons-weaver-privilizer-api:2.0 | privilizer 注解(@Privileged/@Privilizing) | privilizer 场景必须 |
commons-weaver-privilizer:2.0 | privilizer 织入提供者(重写特权块) | privilizer 场景必须 |
commons-weaver-normalizer:2.0 | normalizer 织入提供者(收敛匿名类) | 可选 |
commons-weaver-maven-plugin:2.0 | Maven 插件(weave/prepare 等 goal) | Maven 方案必须 |
commons-weaver-antlib:2.0 | Ant 任务 | 可选 |
⚠️ 注意:社区资料里提到的
commons-weaver-proxy「动态代理织入提供者」在 2.0 发布物与 Maven 中央仓库中均不存在(https://repo1.maven.org/maven2/org/apache/commons/commons-weaver-proxy/2.0/返回 404,bin 包内亦无此 jar),不要按该名称引用。
JDK 兼容性实测结论(选型关键):织入器运行在哪个 JDK 上执行织入与产物跑在哪个 JRE 上是两回事:
| 环节 | JDK 8 | JDK 17 |
|---|---|---|
| 编译演示工程(产物字节码 major 52) | 通过 | 通过 |
执行织入(commons-weaver:weave) | 通过 | 失败 |
| 运行织入产物 | 通过 | 通过(字节码 major 52,JRE 向下兼容) |
JDK 17 上织入的真实报错:
[ERROR] Failed to execute goal org.apache.commons:commons-weaver-maven-plugin:2.0:weave (default) on project privilizer-demo: weaving failed due to java.lang.IllegalStateException: java.lang.IllegalArgumentException: Unsupported class file major version 61 -> [Help 1]
根因:privilizer 织入时需要读取被调用方类文件(包括 JDK 自身的 java/lang/System 等)判断可访问性,其内嵌的 ASM 6.2.1(以 shaded 形式打包在 privilizer jar 内,无法通过替换依赖升级)不认识 JDK 17 的 major 61 字节码。因此执行织入的构建环境必须使用 JDK 8,本文按此编写。若希望构建期用 JDK 17、仅织入步骤用 JDK 8,可在 Maven 工具链中为 commons-weaver-maven-plugin 单独指定 JDK 8(toolchains.xml),本文不展开。
四、方案 A:Maven 依赖安装(在线,推荐)
本节通过 apt 安装 JDK 8 与 Maven,再用 Maven 坐标引入 Weaver 模块并跑通 privilizer 织入最小验证工程。
4.1 方案说明
方案 A 的链路是:apt 安装 openjdk-8-jdk 与 maven → 切换系统默认 JDK 到 8 → 工程 pom.xml 中声明 commons-weaver-privilizer-api(编译期注解)与 commons-weaver-maven-plugin(插件级依赖带入 privilizer 织入器)→ mvn package 时在 process-classes 阶段自动织入 → 运行验证。
4.2 安装 JDK 8 并切换默认版本
apt-get update && apt-get install -y openjdk-8-jdk
# 输出节选(实测首尾)
Setting up openjdk-8-jdk:amd64 (8u504-ga-1ubuntu1~22.04.3) ...
安装后确认版本与包信息:
apt-cache policy openjdk-8-jdk maven | grep -E 'openjdk-8-jdk:|maven:|Installed:' | head -4
openjdk-8-jdk:
Installed: 8u504-ga-1ubuntu1~22.04.3
maven:
Installed: 3.6.3-5
Ubuntu 22.04 上执行 apt install openjdk-8-jdk maven 后,系统默认 java 仍指向 OpenJDK 11(maven 会连带安装 default-jdk),必须切换 alternatives,否则织入时会遇到第三章的 major version 报错(JDK 11 字节码 major 55 同样不被 ASM 6.2.1 支持)。实测切换过程:
java -version 2>&1 | head -1
openjdk version "11.0.32.1" 2026-08-18
update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac
java -version 2>&1
update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java to provide /usr/bin/java (java) in manual mode
openjdk version "1.8.0_504"
OpenJDK Runtime Environment (build 1.8.0_504-8u504-ga-1ubuntu1~22.04.3-b01)
OpenJDK 64-Bit Server VM (build 25.504-b01, mixed mode)
验证点:java -version 输出 1.8.0_504 且无 11.0.x,说明默认 JVM 已切到 JDK 8。
4.3 确认 Maven 绑定 JDK 8
mvn -v | head -3
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 1.8.0_504, vendor: Ubuntu, runtime: /usr/lib/jvm/java-8-openjdk-amd64/jre
验证点:Java version 一行显示 1.8.0_504,说明 Maven 已绑定 JDK 8(apt 版 Maven 经 PATH 中的 java 定位 JDK,alternatives 切换即生效,无需设置 JAVA_HOME)。
4.4 创建最小验证工程
创建工程目录:
mkdir -p /work/mini-demo/src/main/java/demo
/work/mini-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>weaver-mini</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-weaver-privilizer-api</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>org.apache.commons</groupId>
<artifactId>commons-weaver-maven-plugin</artifactId>
<version>2.0</version>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-weaver-privilizer</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
<configuration>
<weaverConfig>
<privilizer.policy>ALWAYS</privilizer.policy>
<privilizer.accessLevel>public</privilizer.accessLevel>
</weaverConfig>
</configuration>
<executions>
<execution>
<goals>
<goal>weave</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
逐项说明关键配置:
commons-weaver-privilizer-api:2.0:编译期注解来源(@Privileged),工程依赖声明一次即可。- 插件级
<dependencies>中的commons-weaver-privilizer:2.0:织入提供者必须挂在commons-weaver-maven-plugin的<dependencies>下。Weaver 插件通过 ServiceLoader 从插件自身的类加载空间发现织入提供者;若把它写成普通工程依赖,插件发现不了任何提供者,weavegoal 会「成功」执行但一个类都不改(实测复现,详见 FAQ Q2)。 weaverConfig:织入参数键值对。privilizer.policy=ALWAYS表示织入为无条件doPrivileged块;privilizer.accessLevel=public允许@Privileged标注在 public 方法上(默认值private,标注更宽访问级别的方法会直接报错,见 FAQ Q3)。weavegoal 默认绑定process-classes阶段,mvn package自动触发。
4.5 最小验证代码
/work/mini-demo/src/main/java/demo/SecretStore.java(特权方法提供者):
package demo;
import org.apache.commons.weaver.privilizer.Privileged;
/**
* 特权方法提供者:readToken 的方法体需要以特权方式读取系统属性。
*/
public final class SecretStore {
private SecretStore() {
}
@Privileged
public static String readToken() {
return System.getProperty("demo.token", "unset");
}
}
/work/mini-demo/src/main/java/demo/ConfigClient.java(普通调用方):
package demo;
/**
* 普通调用方:直接调用特权方法,自身不做任何声明。
*/
public final class ConfigClient {
private ConfigClient() {
}
public static String token() {
return SecretStore.readToken();
}
}
/work/mini-demo/src/main/java/demo/Main.java(入口):
package demo;
public final class Main {
private Main() {
}
public static void main(String[] args) {
System.out.println("ConfigClient.token() = " + ConfigClient.token());
System.out.println("SecurityManager active = " + (System.getSecurityManager() != null));
}
}
4.6 构建织入与运行验证
cd /work/mini-demo && mvn -B clean package 2>&1 | grep -E 'weave|security leak|BUILD|Total time'
[INFO] --- commons-weaver:2.0:weave (default) @ weaver-mini ---
[WARNING] Possible security leak: granting privileges to public method demo/SecretStore.readToken()Ljava/lang/String;
[INFO] BUILD SUCCESS
[INFO] Total time: 0.974 s
回显解读:weave (default) 是织入 goal 在 process-classes 阶段执行;Possible security leak 警告来自 privilizer 自身的访问级别审计——它提醒「把特权授予 public 方法等于任何能调用该方法的人都获得特权」,属预期提示而非错误,介意可把 @Privileged 方法改为包级私有并把 privilizer.accessLevel 配回 private。
运行织入后的程序:
java -Ddemo.token=secret-123 -cp target/classes demo.Main
ConfigClient.token() = secret-123
SecurityManager active = false
用 javap 验证织入确实发生(未织入的 readToken 只有一条 System.getProperty 调用):
javap -p -c target/classes/demo/SecretStore.class | grep -E 'readToken_ACTION|doPrivileged|__privileged'
0: new #34 // class demo/SecretStore$readToken_ACTION
4: invokespecial #36 // Method demo/SecretStore$readToken_ACTION."<init>":()V
7: invokestatic #42 // Method java/security/AccessController.doPrivileged:(Ljava/security/PrivilegedAction;)Ljava/lang/Object;
static java.lang.String __privileged_access$0();
0: invokestatic #48 // Method __privileged_readToken:()Ljava/lang/String;
private static java.lang.String __privileged_readToken();
结果解读:原方法体被搬进合成方法 __privileged_readToken(),readToken() 本身被改写为「构造 SecretStore$readToken_ACTION 特权动作 → AccessController.doPrivileged 执行 → 强转返回」。这证明 privilizer 织入提供者在构建期真实生效,且运行产物不依赖任何 Weaver jar(-cp target/classes 即可运行)。
五、方案 B:官方 bin 包 + 手动 classpath 直跑(离线友好)
本节面向没有 Maven/构建体系的场景:下载官方 bin 包,用 javac/java 手动拼 classpath 完成编译、织入、运行。
5.1 方案说明
链路是:下载并校验 bin 包 → 解压得到 6 个 Weaver 模块 jar → 从中央仓库补齐 3 个传递依赖 jar(xbean-finder、xbean-asm6、commons-lang3)→ javac 编译演示源码 → 用一个 20 行的 WeaveRunner 类直调 WeaveProcessor 完成织入 → java 运行。适用于离线环境或想把织入嵌进自有构建脚本的读者。
5.2 下载与完整性校验
mkdir -p /work/planb && cd /work/planb
curl -sO --max-time 120 https://downloads.apache.org/commons/weaver/binaries/commons-weaver-2.0-bin.zip
curl -sO --max-time 60 https://downloads.apache.org/commons/weaver/binaries/commons-weaver-2.0-bin.zip.sha256
ls -l commons-weaver-2.0-bin.zip
cat commons-weaver-2.0-bin.zip.sha256
sha256sum commons-weaver-2.0-bin.zip
-rw-r--r-- 1 root root 2146159 Oct 15 03:02 commons-weaver-2.0-bin.zip
f5ec5a73d8b3a43b44b31e3e0eea9249d444214579dd649b03ad171b527603f2
f5ec5a73d8b3a43b44b31e3e0eea9249d444214579dd649b03ad171b527603f2 commons-weaver-2.0-bin.zip
验证点:.sha256 文件中的期望值与 sha256sum 实测值逐字一致;不一致必须删除重下。
5.3 解包与补齐传递依赖
cd /work/planb && unzip -q -o commons-weaver-2.0-bin.zip && ls commons-weaver-2.0/
LICENSE-asm6.2.1.txt
LICENSE.txt
NOTICE.txt
RELEASE-NOTES.txt
commons-weaver-antlib-2.0.jar
commons-weaver-maven-plugin-2.0.jar
commons-weaver-privilizer-2.0.jar
commons-weaver-privilizer-api-2.0.jar
commons-weaver-processor-2.0.jar
commons-weaver-normalizer-2.0.jar
💡 说明:bin 包只含 Weaver 自身 6 个模块 jar,不含织入器的传递依赖。命令行直跑还需从 Maven 中央仓库下载三个 jar:
xbean-finder-shaded-4.9.jar、xbean-asm6-shaded-4.9.jar(注解扫描)与commons-lang3-3.7.jar(processor 内部使用),本文已实测下载并验证可用:
cd /work/planb
M=https://repo1.maven.org/maven2
curl -sO --max-time 60 $M/org/apache/xbean/xbean-finder-shaded/4.9/xbean-finder-shaded-4.9.jar
curl -sO --max-time 60 $M/org/apache/xbean/xbean-asm6-shaded/4.9/xbean-asm6-shaded-4.9.jar
curl -sO --max-time 60 $M/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar
5.4 编写织入入口并编译演示源码
/work/planb/WeaveRunner.java(完整内容):
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.weaver.WeaveProcessor;
public class WeaveRunner {
public static void main(String[] args) throws Exception {
final String target = args[0];
final List<String> classpath = new ArrayList<>(java.util.Arrays.asList(args[1].split(":")));
final Properties config = new Properties();
config.setProperty("privilizer.policy", "ALWAYS");
config.setProperty("privilizer.accessLevel", "public");
new WeaveProcessor(classpath, new File(target), config).weave();
System.out.println("WEAVE_DONE");
}
}
准备演示源码(可直接复制 4.5 节三个类到 /work/planb/src/demo/),然后编译:
cd /work/planb && mkdir -p classes
R=commons-weaver-2.0
javac -encoding UTF-8 -cp $R/commons-weaver-privilizer-api-2.0.jar -d classes src/demo/*.java
javac -encoding UTF-8 -cp $R/commons-weaver-processor-2.0.jar:$R/commons-weaver-privilizer-2.0.jar -d classes WeaveRunner.java
JAVAC_OK
⚠️ 注意:
javac不带-encoding UTF-8时,源码中的中文注释在默认 POSIX locale 下会报error: unmappable character for encoding ASCII(本文实测复现过 69 errors),这是编码问题不是语法问题,加参数即可解决。
5.5 执行织入并运行
织入器 classpath 由 6 个 jar 组成(3 个 Weaver 模块 + 3 个传递依赖):
cd /work/planb
R=commons-weaver-2.0
WC=$R/commons-weaver-processor-2.0.jar:$R/commons-weaver-privilizer-2.0.jar:$R/commons-weaver-privilizer-api-2.0.jar:xbean-finder-shaded-4.9.jar:xbean-asm6-shaded-4.9.jar:commons-lang3-3.7.jar
java -cp classes:$WC WeaveRunner /work/planb/classes $WC 2>&1 | grep -E 'PROVIDER|WARNING|WEAVE_DONE' | head -5
PROVIDER: org.apache.commons.weaver.privilizer.PrivilizerWeaver
WARNING: Possible security leak: granting privileges to public method demo/SecretStore.readToken()Ljava/lang/String;
WEAVE_DONE
回显解读:PROVIDER 一行说明 ServiceLoader 从 classpath 发现了 privilizer 织入提供者(bin 包 jar 内的 META-INF/services/org.apache.commons.weaver.spi.Weaver 注册文件生效);WEAVE_DONE 表示织入完成。
运行织入产物(运行期 classpath 只有 classes,无任何 Weaver jar):
java -Ddemo.token=secret-123 -cp /work/planb/classes demo.Main
ConfigClient.token() = secret-123
SecurityManager active = false
验证点:与方案 A 结果一致,证明方案 A/B 只是织入的触发方式不同,产物等价。
六、方案对比与选择建议
| 维度 | 方案 A:Maven 插件 | 方案 B:bin 包 + 手动 classpath |
|---|---|---|
| 织入触发 | process-classes 阶段自动执行 | 自行调用 WeaveProcessor.weave() |
| 依赖管理 | Maven 自动解析(含传递依赖) | 手工收集 3 个传递依赖 jar |
| 与构建集成 | 无缝,mvn package 一步到位 | 需自写脚本拼 classpath |
| 离线可用性 | 首次需联网下载依赖 | 下载完成后完全离线 |
| 适用场景 | 常规工程开发(推荐) | 无构建体系环境、脚本化定制织入 |
选型建议:有 Maven/Gradle 构建体系一律选方案 A(Gradle 可用 Ant 任务 commons-weaver-antlib 桥接,本文不展开);把织入嵌入自研打包流水线时选方案 B,WeaveRunner 的三个入参(目标目录、classpath、织入配置)足够脚本化。
七、配置说明
Weaver 没有独立配置文件,全部配置通过 Maven 插件的 weaverConfig(Properties 键值对)或 WeaveProcessor 构造参数传入。实测有效的配置键:
| 配置键 | 取值 | 说明 |
|---|---|---|
privilizer.policy | NEVER / ON_INIT / DYNAMIC(默认) / ALWAYS | ALWAYS 织入为无条件 doPrivileged;DYNAMIC 每次 @Privileged 方法执行时检查是否安装了 SecurityManager 再决定;ON_INIT 只在初始化时检查一次;NEVER 关闭织入 |
privilizer.accessLevel | public / protected / package / private(默认) | 允许 @Privileged 标注的最大访问级别,超出即报错 |
privilizer.verify | true / false | 织入后是否用 ASM 的 CheckClassAdapter 校验产物字节码(取自模块源码,本文未单独实测该开关) |
normalizer.targetPackage | 合法包名 | normalizer 把合格匿名类重映射到该包下的 $normalized<摘要> 规范位置 |
环境变量方面,apt 安装的 Maven 经 PATH 中的 java(alternatives 指向 JDK 8)定位 JDK,实测 JAVA_HOME 为空也能构建;若机器上有多套 JDK 且希望显式固定,可导出:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 && echo "JAVA_HOME=$JAVA_HOME"
JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
八、常见问题(FAQ)
Q1:织入时报 Unsupported class file major version 61 怎么办?
这是在 JDK 17(或 11)上执行 weave 的典型报错:privilizer 判断方法可访问性时要读取 JDK 自身类文件,内嵌的 ASM 6.2.1 只支持到 Java 8 时代的字节码(major 52),遇到 major 61 即抛错。解法:把执行织入的构建环境换成 JDK 8(update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 切换后用 mvn -v 确认 Java version: 1.8.0_xxx)。注意产物字节码是 major 52,JDK 17 的 JRE 仍可运行,所以「构建用 8、运行用 17」在字节码层面是可行的,但 privilizer 的安全语义依赖 SecurityManager,而 SecurityManager 自 JDK 17 起已标记废弃,长期规划请参考官方 JEP 411 的迁移指引。
Q2:mvn package 显示 BUILD SUCCESS 但 javap 发现类根本没被织入?
最常见原因:把 commons-weaver-privilizer 写成了普通工程依赖。Weaver 插件通过 ServiceLoader 从插件自身的类加载空间发现织入提供者,工程依赖进入不了这个空间,于是 weave goal 静默空转。解法:把该依赖挪到插件级 <dependencies>(见 4.4 节 pom),重跑 mvn clean package。
Q3:报 must have maximum access level 'private' but is defined wider ('public')?
@Privileged 默认只允许标注 private 方法,标注在 public/protected 方法上且未放宽配置时织入失败。解法:weaverConfig 增加 <privilizer.accessLevel>public</privilizer.accessLevel>。放宽后织入会输出 Possible security leak 警告——这是有意的安全提示,评估接受即可。
Q4:javac 报大量 unmappable character for encoding ASCII?
POSIX locale 下 javac 默认按 ASCII 解码源码,中文注释必报错。解法:javac -encoding UTF-8;Maven 工程则确认 pom 里有 project.build.sourceEncoding=UTF-8。
Q5:运行时想验证特权块真的生效,为什么加了 -Djava.security.manager 还是没差别?
SecurityManager 默认策略下不会拒绝普通代码读系统属性,需要配合自定义 policy 限制调用方权限才能观察到「织入前 AccessControlException、织入后正常返回」的差异。完整对照实验(分离 codeSource + policy 文件 + 前后栈回显)见《Apache Commons Weaver 2.0 织入示例与最佳实践详解》第三章。
Q6:可以用 -javaagent 在运行期织入吗?
不可以。Commons Weaver 2.0 只提供构建期织入,官方发布物中没有 Java agent jar,-javaagent 方式无从谈起。需要类加载期织入请评估 AspectJ LTW 等方案(对比见第一章技术拓展表)。
九、卸载与清理
移除验证工程与下载产物:
rm -rf /work/mini-demo /work/planb
ls /work/mini-demo 2>&1
ls: cannot access '/work/mini-demo': No such file or directory
清理 Maven 本地仓库中的 Weaver 模块(影响所有工程,慎用):
rm -rf $HOME/.m2/repository/org/apache/commons/commons-weaver-*
卸载 JDK 8 与 Maven(仅当不再需要,先 -s 预演确认):
apt-get -s purge -y openjdk-8-jdk maven | grep -E 'REMOVED|Purg' | head -4
The following packages will be REMOVED:
maven* openjdk-8-jdk*
Purg maven [3.6.3-5]
Purg openjdk-8-jdk [8u504-ga-1ubuntu1~22.04.3]
确认无误后真正卸载:
apt-get purge -y openjdk-8-jdk maven && apt-get autoremove -y
十、参考链接
- Apache Commons Weaver 官方主页:https://commons.apache.org/proper/commons-weaver/
- Apache Commons Weaver 官方下载目录:https://downloads.apache.org/commons/weaver/
- Commons Weaver 2.0 发布说明:https://downloads.apache.org/commons/weaver/RELEASE-NOTES.txt
- Maven 中央仓库坐标页:https://repo1.maven.org/maven2/org/apache/commons/commons-weaver-processor/
- Weaver Maven 插件文档:https://commons.apache.org/proper/commons-weaver/commons-weaver-maven-plugin/
- OpenJDK 8 安装包(Ubuntu 官方源):
apt-cache show openjdk-8-jdk