Ubuntu 22.04 安装 Apache Commons JEXL 指南
在 Ubuntu 22.04 上从零引入 Apache Commons JEXL 3.7.0,覆盖 JDK 17 与 Maven 准备、Maven 依赖、官方二进制包离线安装与源码编译三套方案,附真实回显与 3.7.0 行为变化排障。
Ubuntu 22.04 安装 Apache Commons JEXL 指南
目标:在 Ubuntu 22.04 上从零搭好 Apache Commons JEXL 3.7.0 的使用环境,完成 JDK 17 + Maven 准备、Maven 依赖引入、官方二进制包离线安装与源码编译三种获取方式,并跑通真实表达式求值验证。以下全部命令与回显均已在 Ubuntu 22.04(x86_64)实测通过。
一、环境信息
本节先固定本次实测所用软硬件基线,便于读者逐项对照复现。
- 操作系统:Ubuntu 22.04(x86_64)
- JDK:OpenJDK 17.0.20.1(
java -version实测) - 构建工具:Apache Maven 3.6.3(系统包仓库默认版本)
- 目标库:Apache Commons JEXL 3.7.0(
org.apache.commons:commons-jexl3:3.7.0,2026-07 发布) - 传递依赖:commons-logging 1.4.0(有且仅有一个,实测依赖树确认)
- 依赖来源:
https://downloads.apache.org/commons/jexl/、https://repo1.maven.org/maven2/(网络直连可达) - Maven 示例工程目录:
/opt/jexl-demo - 官方二进制包解压目录:
/opt/jexl3-dist - 二进制包验证程序目录:
/opt/jexl3-demo - 源码编译目录:
/opt/commons-jexl3-3.7.0-src
先确认 JDK 与 Maven 的版本基线,后续所有编译运行都基于它:
java -version 2>&1
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。
二、前置条件
在开始安装前,请确认以下条件满足,否则后续步骤会因权限或网络问题中断。
- root 权限或 sudo:安装 JDK、Maven 与写入
/opt目录需要相应权限。 - 网络可达:需要访问
downloads.apache.org(官方二进制/源码包)与repo1.maven.org(Maven 中央仓库);网络连通性将在 4.3 的依赖下载与 5.2、6.2 的官方包下载时随实际命令自然验证。 - 磁盘空间:官方二进制包与源码包均为 MB 级,加上本地 Maven 仓库缓存建议预留 ≥ 200 MB。
- 纯 Java 库:JEXL 无本地原生依赖,x86_64 之外的架构同样可用。
⚠️ 注意:若内网无法直连上述域名,需按企业代理配置放行,否则 Maven 依赖与官方包均无法获取。本文实测环境为直连。
三、版本选型说明
Apache Commons JEXL(Java Expression Language)是 Apache 基金会的 Java 表达式引擎:在运行期把表达式字符串(如 1 + 2 * 3、user.age > 18 ? '成年' : '未成年')解析为语法树并求值,常用于规则引擎、动态告警条件、工作流条件分支与文本模板渲染。
💡 技术拓展:JEXL 属于「Java 表达式引擎」赛道,同一赛道还有多个常见选项,选型前应了解全貌——
引擎 语法风格 安全模型 性能/形态 适用场景 Apache Commons JEXL 类 Java / JSP EL,支持脚本块与模板 JexlPermissions白名单,3.x 起默认收紧轻量纯库,表达式缓存,依赖极少 规则引擎、动态条件、模板渲染 Spring SpEL 类 Java,可引用 Bean 与方法 StandardEvaluationContext全开 /SimpleEvaluationContext受限与 Spring 生态深度集成 Spring 应用内注解与配置表达式 OGNL 导航链式语法 早期默认宽松,需自行封锁 成熟但年代较早 Struts/WebWork 遗留系统 MVEL 类 Java 脚本 + 模板 默认宽松 以运行时字节码生成著称 Drools 等规则引擎内嵌脚本 Groovy 脚本 完整 JVM 动态语言 SecureASTCustomizer白名单沙箱编译为字节码,能力最强也更重 复杂内嵌脚本、DSL 本文属于 Java 服务内轻量动态求值场景,选用 JEXL 3.7.0:Apache 出品、运行期依赖仅 commons-logging 一个、提供「单表达式 → 多语句脚本 → 模板」完整梯度,且
JexlPermissions权限模型适合把动态求值安全地开放给外部输入。
JEXL 3.x 版本线从 3.0 演进至 3.7.0(2026-07 发布),选型对照如下:
| 版本 | 状态 | 建议 |
|---|---|---|
| 3.7.0 | 当前最新正式版(2026-07) | 新项目首选;注意 3.7.0 的默认权限收紧等行为变化(见下表) |
| 3.0 – 3.6 | 历史版本 | 仅供存量系统维护;新项目不建议 |
3.7.0 相比旧版有几处实测踩到的重大 API/行为变化,从旧版升级或按旧教程编写时务必对照:
| 变化点 | 旧版行为 | 3.7.0 实测行为 |
|---|---|---|
| 自定义函数注册 | JexlBuilder.setFunctions(Map) | 改为 JexlBuilder.namespaces(Map),旧方法已不存在(编译报 cannot find symbol) |
| 默认反射权限 | 较宽松,POJO 属性可直接导航 | 默认收紧:白名单外 POJO 属性/方法被拦截,需显式 JexlPermissions.UNRESTRICTED |
| 脚本全局赋值 | 允许对未声明变量直接赋值 | 禁止:抛 JexlException$Feature: global assign/modify error,必须用 var 声明 |
| 模板指令前缀 | %{if} / #{if} | 改为 $$ 行前缀,旧式写法解析报错或被当作普通文本 |
| 源码构建要求 | Maven 3.x 即可 | enforcer 强制 Maven ≥ 3.9,低版本构建被拒 |
💡 说明:这些变化点的真实报错回显会在本文第四、六、九章与《Apache Commons JEXL 3.7.0 配置参数详解》《Apache Commons JEXL 3.7.0 常用 API 与表达式语法详解》两篇中逐条出现,遇到对应异常时可直接对照排查。
JEXL 的 Maven 坐标为 org.apache.commons:commons-jexl3。查询中央仓库 3.7.0 的依赖解析结果(mvn dependency:tree,工程创建见 4.3):
cd /opt/jexl-demo && mvn dependency:tree
输出节选(实测首尾):
[INFO]
[INFO] ---------------------------< demo:jexl-demo >---------------------------
[INFO] Building jexl-demo 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ jexl-demo ---
[INFO] demo:jexl-demo:jar:1.0.0
[INFO] \- org.apache.commons:commons-jexl3:jar:3.7.0:compile
[INFO] \- commons-logging:commons-logging:jar:1.4.0:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] Total time: 0.821 s
解读:commons-jexl3 3.7.0 不是零依赖库——有且仅有一个运行时传递依赖 commons-logging:1.4.0。Maven 会自动解析它;手动 jar 方式(方案 B)必须自行补齐该 jar,否则运行时报 NoClassDefFoundError(见 4.7 与 FAQ Q1)。
官方发行包位于 downloads.apache.org 的 binaries/(二进制)与 source/(源码)子目录,本文分别在方案 B、C 中实测下载。
四、在线安装方案(A:Maven 依赖引入)
官方推荐方式:通过 Maven 引入 commons-jexl3:3.7.0,由构建工具自动解析传递依赖。适合可联网的开发/构建环境。
4.1 方案说明与适用场景
本方案在系统确认 JDK 17 与 Maven 可用后,创建一个最小 Maven 工程引入 commons-jexl3:3.7.0,编译并运行一个真实表达式求值程序完成验证。适合持续集成、可访问中央仓库的场景。
4.2 安装 JDK 17 与 Maven
Ubuntu 22.04 官方源提供 openjdk-17-jdk 与 maven 包。若系统尚未安装,执行:
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y openjdk-17-jdk maven
本次实测环境两项均已就绪,安装后(或已预装时)用 1.1 节同款命令验证:
java -version 2>&1
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、mvn -v 的 Java version 同为 17.0.20.1,说明 JDK 17 与 Maven 均可用且 Maven 已绑定 JDK 17,可继续。
4.3 创建 Maven 工程并引入依赖
创建工程目录:
mkdir -p /opt/jexl-demo/src/main/java/demo
/opt/jexl-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>jexl-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-jexl3</artifactId>
<version>3.7.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>
</configuration>
</plugin>
</plugins>
</build>
</project>
逐项说明:
project.build.sourceEncoding=UTF-8:源码编码。示例代码含中文注释/字符串,必须显式声明,否则编译器按平台默认编码处理会报unmappable character。maven.compiler.release=17:用 JDK 17 的跨版本编译语义产出 release 17 字节码(JEXL 3.7.0 在 JDK 17 上实测正常)。dependencies:仅声明commons-jexl3:3.7.0一项;commons-logging:1.4.0由 Maven 传递解析,无需手写。maven-compiler-plugin显式固定 3.11.0 并配置<release>17</release>,避免旧默认插件忽略 release 参数。
4.4 编写最小验证程序
/opt/jexl-demo/src/main/java/demo/BasicEval.java 全文:
package demo;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.MapContext;
/** 算术、字符串、三元表达式求值 */
public class BasicEval {
public static void main(String[] args) {
JexlEngine jexl = new org.apache.commons.jexl3.JexlBuilder().create();
Object r1 = jexl.createExpression("1 + 2 * 3").evaluate(null);
System.out.println("1 + 2 * 3 = " + r1);
Object r2 = jexl.createExpression("(1 + 2) * 3").evaluate(null);
System.out.println("(1 + 2) * 3 = " + r2);
Object r3 = jexl.createExpression("10 / 4").evaluate(null);
System.out.println("10 / 4 = " + r3);
Object r4 = jexl.createExpression("10 % 3").evaluate(null);
System.out.println("10 % 3 = " + r4);
// 字符串拼接
Object r5 = jexl.createExpression("'Hello, ' + 'JEXL' + ' 3.7.0'").evaluate(null);
System.out.println("concat: " + r5);
// 三元表达式
Object r6 = jexl.createExpression("5 > 3 ? 'yes' : 'no'").evaluate(null);
System.out.println("ternary: " + r6);
// 逻辑与比较
Object r7 = jexl.createExpression("!(5 > 3) && (2 == 2)").evaluate(null);
System.out.println("logic: " + r7);
// 字符串方法
Object r8 = jexl.createExpression("'jexl-demo'.toUpperCase()").evaluate(null);
System.out.println("string method: " + r8);
}
}
说明:new JexlBuilder().create() 创建默认引擎;createExpression("1 + 2 * 3") 这类调用解析表达式,evaluate(null) 在无上下文变量时求值(纯运算表达式不需要变量)。该程序覆盖算术、字符串、三元与逻辑四类语法,输出全部可心算校验。
4.5 编译验证
cd /opt/jexl-demo && mvn compile
输出节选(实测首尾):
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------------< demo:jexl-demo >---------------------------
[INFO] Building jexl-demo 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jexl-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/jexl-demo/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ jexl-demo ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 8 source files with javac [debug release 17] to target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.805 s
[INFO] Finished at: 2026-10-14T10:22:34Z
[INFO] ------------------------------------------------------------------------
解读:BUILD SUCCESS 说明依赖解析与编译全部通过。Compiling 8 source files 表示实测工程共含 8 个演示类(BasicEval、ContextVar、PojoNav、CollectionEval、ScriptDemo、TemplateDemo、FunctionNS、PermissionDemo,后三类用于《常用 API 与表达式语法详解》与《快速入门与集成示例》两篇);读者初次只写 BasicEval.java 一个文件时,此处显示 Compiling 1 source files 属正常。
⚠️ 注意:若此时编译报
cannot find symbol: method setFunctions,说明用了旧版教程的函数注册 API——3.7.0 已改为namespaces,见 FAQ Q2。
4.6 生成运行 classpath
Maven 编译产物在 target/classes,运行还需把依赖 jar 拼进 classpath。用 dependency:build-classpath 导出到文件:
cd /opt/jexl-demo && mvn dependency:build-classpath -Dmdep.outputFile=cp.txt -q && cat cp.txt
输出:
/root/.m2/repository/org/apache/commons/commons-jexl3/3.7.0/commons-jexl3-3.7.0.jar:/root/.m2/repository/commons-logging/commons-logging/1.4.0/commons-logging-1.4.0.jar
解读:classpath 共两个 jar——JEXL 本体加上 commons-logging-1.4.0。这再次印证 3.7.0 的唯一传递依赖;java -cp 直跑时若漏掉第二个 jar 就会报 NoClassDefFoundError(见 4.7)。
4.7 运行验证与排障
运行 demo.BasicEval:
cd /opt/jexl-demo
CP="target/classes:$(cat cp.txt)" && java -Dfile.encoding=UTF-8 -cp "$CP" demo.BasicEval
输出:
1 + 2 * 3 = 7
(1 + 2) * 3 = 9
10 / 4 = 2
10 % 3 = 1
concat: Hello, JEXL 3.7.0
ternary: yes
logic: false
string method: JEXL-DEMO
验证点:1 + 2 * 3 = 7(先乘后加)、(1+2)*3 = 9、10/4 = 2(整数除法截断)、10%3 = 1、字符串拼接与 toUpperCase()、三元与逻辑取反全部正确,说明 JEXL 3.7.0 已正确引入并求值。-Dfile.encoding=UTF-8 保证含中文的场景输出不乱码。
⚠️ 注意(实测排障):若手工拼 classpath 时只放了
commons-jexl3-3.7.0.jar、漏掉commons-logging-1.4.0.jar,运行即报(实测原文,中间堆栈帧略):Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory at org.apache.commons.jexl3.internal.introspection.Uberspect.<init>(Uberspect.java:105) ... Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory解决:把
commons-logging-1.4.0.jar一并加入-cp(如上CP变量做法),或改用mvn exec/ shade 打包等方式由构建工具管理依赖。
至此方案 A 完成:JEXL 3.7.0 已通过 Maven 引入并跑通真实求值。
五、离线安装方案(B:官方二进制包)
适合内网隔离、无法访问中央仓库的运行环境。使用官方 bin 发行包 + 手动补齐传递依赖 jar。
5.1 方案说明与适用场景
官方 commons-jexl-3.7.0-bin.tar.gz 内含 JEXL 本体 jar、javadoc、sources 与离线 apidocs,但不含 commons-logging-1.4.0.jar(3.7.0 的唯一运行时依赖)。因此本方案流程为:下载官方包 → 解压 → 取得 commons-logging jar → javac/java 直跑验证。
5.2 下载与解压
mkdir -p /opt/jexl3-dist && cd /opt && curl -LO https://downloads.apache.org/commons/jexl/binaries/commons-jexl-3.7.0-bin.tar.gz
tar xzf commons-jexl-3.7.0-bin.tar.gz -C /opt/jexl3-dist --strip-components=1
find /opt/jexl3-dist -name "*.jar" | sort
输出:
/opt/jexl3-dist/commons-jexl3-3.7.0-javadoc.jar
/opt/jexl3-dist/commons-jexl3-3.7.0-sources.jar
/opt/jexl3-dist/commons-jexl3-3.7.0-test-sources.jar
/opt/jexl3-dist/commons-jexl3-3.7.0-tests.jar
/opt/jexl3-dist/commons-jexl3-3.7.0.jar
解压目录整体内容:
ls -la /opt/jexl3-dist/
输出节选(实测):
LICENSE.txt NOTICE.txt RELEASE-NOTES.txt apidocs/
commons-jexl3-3.7.0-javadoc.jar
commons-jexl3-3.7.0-sources.jar
commons-jexl3-3.7.0-test-sources.jar
commons-jexl3-3.7.0-tests.jar
commons-jexl3-3.7.0.jar
解读:核心运行 jar 是 commons-jexl3-3.7.0.jar;apidocs/ 目录与 -javadoc.jar 提供离线 API 文档,-sources.jar 可在 IDE 中直接反查源码——离线环境下排查 API 问题时非常有用。
5.3 补齐 commons-logging 依赖
二进制包不附带 commons-logging jar。实测环境中它已由方案 A 的 Maven 解析下载到本地仓库 /root/.m2/repository/commons-logging/commons-logging/1.4.0/commons-logging-1.4.0.jar(4.6 节 cat cp.txt 的回显即为该路径);若本机从未执行过方案 A,从中央仓库取同一坐标(commons-logging:commons-logging:1.4.0)的 jar 放入任意目录并加入 classpath 即可。下载后用 ls -l /root/.m2/repository/commons-logging/commons-logging/1.4.0/commons-logging-1.4.0.jar 这类命令确认文件在位,再进入 5.5 的运行步骤。
5.4 编写最小验证程序
/opt/jexl3-demo/Main.java 全文(独立于 Maven 工程,仅验证官方二进制包可用):
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.MapContext;
/** 官方二进制包方式最小验证 */
public class Main {
public static void main(String[] args) {
System.out.println("JEXL impl version = " + org.apache.commons.jexl3.JexlEngine.class.getPackage().getImplementationVersion());
JexlEngine jexl = new JexlBuilder().create();
System.out.println("1 + 2 * 3 = " + jexl.createExpression("1 + 2 * 3").evaluate(null));
MapContext ctx = new MapContext();
ctx.set("name", "bin-dist-user");
System.out.println(jexl.createExpression("'Hello, ' + name + '!'").evaluate(ctx));
System.out.println(jexl.createScript("var s = 0; for (var i = 1; i <= 10; i = i + 1) { s = s + i; } return s;").execute(ctx));
}
}
说明:程序先打印 jar 内置的实现版本号(校验版本最直接的手段),再依次验证纯表达式、带上下文变量的插值表达式与多语句脚本(注意脚本内 s、i 均用 var 声明,3.7.0 禁止全局赋值,见 FAQ Q3)。
5.5 编译与运行
先编译。含中文注释的源码在默认 US-ASCII 编码下会报错(实测原文,仅节选前 8 行):
Main.java:5: error: unmappable character (0xE5) for encoding US-ASCII
/** ???????????????????????????????????? */
^
Main.java:5: error: unmappable character (0xAE) for encoding US-ASCII
^
...
36 errors
原因:系统 locale 为 POSIX 类取值时 javac 默认按 US-ASCII 读取源码,中文注释无法映射。解决:显式加 -encoding UTF-8 重新编译并运行:
cd /opt/jexl3-demo
javac -encoding UTF-8 -cp /opt/jexl3-dist/commons-jexl3-3.7.0.jar Main.java
java -Dfile.encoding=UTF-8 -cp .:/opt/jexl3-dist/commons-jexl3-3.7.0.jar:/root/.m2/repository/commons-logging/commons-logging/1.4.0/commons-logging-1.4.0.jar Main
输出:
JEXL impl version = 3.7.0
1 + 2 * 3 = 7
Hello, bin-dist-user!
55
验证点:JEXL impl version = 3.7.0 由 jar 内置清单读出,直接证明运行时加载的是 3.7.0;1 + 2 * 3 = 7 与 Hello, bin-dist-user! 验证表达式求值;55 为 1 到 10 求和的脚本结果,验证多语句脚本与 var 声明。classpath 中 .(当前目录下的 Main.class)+ JEXL jar + commons-logging jar 三者缺一不可。
⚠️ 注意:
-encoding UTF-8(编译)与-Dfile.encoding=UTF-8(运行)分别控制源码读取与运行期输出编码,两者都建议固定,避免随 locale 漂移。
至此方案 B 完成:不依赖 Maven、纯 jar 直跑同样可用。
六、源码安装方案(C:官方源码编译)
适合需要定制、调试源码或跟随版本线的场景。使用官方 src 源码包,由 Maven 构建产物 jar。
6.1 方案说明与前置依赖
下载官方源码包并解压后用 Maven 构建。3.7.0 的构建由 enforcer 插件强制要求 Maven ≥ 3.9(实测系统自带的 Maven 3.6.3 被 RequireMavenVersion 规则拒绝),因此本章包含「安装 Maven 3.9.9」步骤;JDK 侧与方案 A 相同使用 JDK 17。
6.2 下载与解压源码包
cd /opt && curl -LO https://downloads.apache.org/commons/jexl/source/commons-jexl-3.7.0-src.tar.gz
tar xzf commons-jexl-3.7.0-src.tar.gz
cd commons-jexl3-3.7.0-src
输出:
-rw-r--r-- 1 root root 609028 Oct 14 10:27 /opt/commons-jexl-3.7.0-src.tar.gz
解压目录为 /opt/commons-jexl3-3.7.0-src(含 BUILDING.txt LICENSE.txt NOTICE.txt RELEASE-NOTES.txt pom.xml src)。源码包实测大小 609028 字节(约 0.6 MB)。
6.3 首次构建:被 enforcer 拒绝(实录排障)
用系统自带 Maven 3.6.3 直接构建,真实失败输出如下(关键行):
cd /opt/commons-jexl3-3.7.0-src && mvn package -DskipTests
输出节选(实测关键行):
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:3.6.2:enforce (enforce-maven-version) on project commons-jexl3:
[ERROR] Rule 0: org.apache.maven.enforcer.rules.version.RequireMavenVersion failed with message:
[ERROR] Detected Maven Version: 3.6.3 is not in the allowed range [3.9,).
解读:RequireMavenVersion 规则要求 Maven 版本落在 [3.9,) 区间,3.6.3 被拒。这不是环境故障,而是 3.7.0 源码工程的硬性构建要求。
6.4 安装 Maven 3.9.9
不动系统包,把 Maven 3.9.9 解压到 /opt 独立使用:
cd /opt
curl -LO https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz
tar xzf apache-maven-3.9.9-bin.tar.gz
/opt/apache-maven-3.9.9/bin/mvn -v | head -2
输出:
Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: /opt/apache-maven-3.9.9
验证点:Apache Maven 3.9.9 满足 [3.9,) 要求;该安装为目录级,不覆盖系统 /usr/share/maven 的 3.6.3,两者可并存。
6.5 用 Maven 3.9.9 重新构建
cd /opt/commons-jexl3-3.7.0-src && /opt/apache-maven-3.9.9/bin/mvn package -DskipTests
输出节选(实测尾部):
[INFO] --- source:3.4.0:test-jar-no-fork (attach-sources) @ commons-jexl3 ---
[INFO]
[INFO] --- cyclonedx:2.9.1:makeAggregateBom (build-sbom-cyclonedx) @ commons-jexl3 ---
[INFO] CycloneDX: Resolving Dependencies
[INFO] CycloneDX: Creating BOM version 1.6 with 1 component(s)
[INFO] CycloneDX: Writing and validating BOM (XML): /opt/commons-jexl3-3.7.0-src/target/commons-jexl3-3.7.0-bom.xml
[INFO] attaching as commons-jexl3-3.7.0-cyclonedx.xml
[INFO] CycloneDX: Writing and validating BOM (JSON): /opt/commons-jexl3-3.7.0-src/target/commons-jexl3-3.7.0-bom.json
[INFO] --- spdx:1.0.3:createSPDX (build-sbom-spdx) @ commons-jexl3 ---
[INFO] Creating SPDX File /opt/commons-jexl3-3.7.0-src/target/site/org.apache.commons_commons-jexl3-3.7.0.spdx.json
[INFO]
[INFO] --- moditect:1.3.0.Final:add-module-info (add-module-infos) @ commons-jexl3 ---
[INFO] Running jdeps --generate-module-info /opt/commons-jexl3-3.7.0-src/target/moditect --add-modules org.apache.commons.logging --module-path /root/.m2/repository/commons-logging/commons-logging/1.4.0/commons-logging-1.4.0.jar --multi-release=9 /opt/commons-jexl3-3.7.0-src/target/commons-jexl3-3.7.0.jar
writing to /opt/commons-jexl3-3.7.0-src/target/moditect/commons.jexl3/versions/9/module-info.java
[INFO] Created module descriptor at /opt/commons-jexl3-3.7.0-src/target/moditect/generated-sources/org.apache.commons.jexl3/module-info.java
[INFO]
[INFO] --- site:3.22.0:attach-descriptor (attach-descriptor) @ commons-jexl3 ---
[INFO] Skipping because packaging 'jar' is not pom.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:16 min
[INFO] Finished at: 2026-10-14T10:45:14Z
[INFO] ------------------------------------------------------------------------
解读:BUILD SUCCESS,实测耗时约 2 分 16 秒(首次构建含联网下载 moditect 等构建插件)。从 moditect 步骤还能看到构建细节:JEXL 会用 jdeps 自动生成 module-info,并显式声明对 org.apache.commons.logging 模块的依赖——再次印证唯一传递依赖是 commons-logging。
6.6 产物验证
ls -la /opt/commons-jexl3-3.7.0-src/target/*.jar
输出:
-rw-r--r-- 1 root root 479163 Oct 14 10:42 target/commons-jexl3-3.7.0-sources.jar
-rw-r--r-- 1 root root 275610 Oct 14 10:42 target/commons-jexl3-3.7.0-test-sources.jar
-rw-r--r-- 1 root root 620819 Oct 14 10:42 target/commons-jexl3-3.7.0-tests.jar
-rw-r--r-- 1 root root 598482 Oct 14 10:42 target/commons-jexl3-3.7.0.jar
验证点:target/commons-jexl3-3.7.0.jar(598482 字节)即编译产物,可直接放入应用 classpath 使用,验证方式同 5.5(把 classpath 换成该路径后运行 Main,输出同样为 JEXL impl version = 3.7.0 起始的 4 行)。
七、方案对比与选择建议
三种方案各有取舍,本节汇总关键差异并给出选型建议。
| 维度 | 方案 A:Maven 依赖 | 方案 B:二进制包 | 方案 C:源码编译 |
|---|---|---|---|
| 是否需联网 | 构建时需联网 | 仅下载阶段需联网 | 构建时需联网(首次下载插件) |
| 安装方式 | 声明依赖自动解析 | 解压 + 手动补 commons-logging jar | enforcer 校验后 Maven 构建 |
| 依赖完整性 | 自动含 commons-logging | 需手动补 1 个 jar | 自动解析 |
| Maven 版本要求 | 3.6.3 实测可用 | 无 | 强制 ≥ 3.9(实测 3.6.3 被拒) |
| 耗时 | 秒级 | 分钟内 | 最长(实测约 2 分 16 秒) |
| 额外产物 | — | javadoc/sources/apidocs | jar + sources + tests + SBOM |
| 适用场景 | CI / 可联网开发 | 内网隔离、离线运行 | 源码定制、调试、跟版本线 |
选择建议:可联网优先方案 A(最简单、传递依赖自动解析);内网隔离选方案 B(一次下载 + 一个依赖 jar 即可离线运行);需要改源码或跟随新版本选方案 C(注意先备好 Maven ≥ 3.9)。三种方式产出的都是同一个 3.7.0 运行时,行为一致,可按场景互换。
八、配置说明
JEXL 是进程内库,无独立配置文件;需要关注的「配置」集中在 JDK/Maven 环境与运行期编码,本节逐项说明。
- JDK 版本:实测在 OpenJDK 17.0.20.1 下编译运行正常;
maven.compiler.release=17的工程需 JDK 17 构建。 - Maven 版本:日常引用 JEXL(方案 A)在 Maven 3.6.3 实测可用;仅方案 C 源码构建被 enforcer 强制要求 Maven ≥ 3.9,建议按 6.4 独立安装 3.9.9。
JAVA_HOME:Ubuntu 22.04 实测系统 Maven 自动绑定 JDK 17,无需设置;若机器并存多个 JDK,mvn -v的Java version行即为生效 JDK,必要时再export JAVA_HOME。- 源码编码:
javac手工编译含中文源码必须-encoding UTF-8(5.5 实测报unmappable character),Maven 工程由project.build.sourceEncoding=UTF-8控制;运行期建议统一-Dfile.encoding=UTF-8。 - 依赖坐标:
org.apache.commons:commons-jexl3:3.7.0,唯一传递依赖commons-logging:commons-logging:1.4.0;手动 jar 方式必须补齐后者。 - 引擎级配置(strict/silent/permissions/namespaces/cache 等
JexlBuilder构建参数):属代码级配置,逐项详解见《Apache Commons JEXL 3.7.0 配置参数详解》。
九、常见问题(FAQ)
Q1:运行报 NoClassDefFoundError: org/apache/commons/logging/LogFactory 怎么办?
classpath 缺 JEXL 3.7.0 的唯一传递依赖 commons-logging 1.4.0。Maven 工程用 mvn dependency:build-classpath -Dmdep.outputFile=cp.txt 生成完整 classpath 后 java -cp "target/classes:$(cat cp.txt)" 运行(见 4.6/4.7);二进制包方式把 commons-logging-1.4.0.jar 一并拼进 -cp(见 5.5)。实测报错原文见 4.7。
Q2:编译报 cannot find symbol: method setFunctions 怎么办?
3.7.0 已移除 JexlBuilder.setFunctions,函数注册改用 namespaces(Map)。实测报错原文:
[ERROR] /opt/jexl-demo/src/main/java/demo/FunctionNS.java:[34,17] cannot find symbol
[ERROR] symbol: method setFunctions(java.util.Map<java.lang.String,java.lang.Object>)
[ERROR] location: class org.apache.commons.jexl3.JexlBuilder
用 javap 反查 3.7.0 真实 API(实测):
javap -cp commons-jexl3-3.7.0.jar org.apache.commons.jexl3.JexlBuilder | grep -Ei "function|namespace|permission"
输出:
public java.util.Map<java.lang.String, java.lang.Object> namespaces();
public org.apache.commons.jexl3.JexlBuilder namespaces(java.util.Map<java.lang.String, java.lang.Object>);
public org.apache.commons.jexl3.introspection.JexlPermissions permissions();
public org.apache.commons.jexl3.JexlBuilder permissions(org.apache.commons.jexl3.introspection.JexlPermissions);
可见只有 namespaces / permissions,没有 setFunctions。把 .setFunctions(funcs) 改为 .namespaces(funcs) 即可。
Q3:脚本里直接写 z = x * y 赋值报错怎么办?
3.7.0 默认禁止对未声明的全局变量赋值,实测报错:
Exception in thread "main" org.apache.commons.jexl3.JexlException$Feature: @1:1 global assign/modify error in 'z'
at org.apache.commons.jexl3.parser.FeatureController.throwFeatureException(FeatureController.java:107)
at org.apache.commons.jexl3.parser.FeatureController.controlSideEffect(FeatureController.java:54)
...
at org.apache.commons.jexl3.JexlEngine.createScript(JexlEngine.java:423)
at demo.ContextVar.main(ContextVar.java:33)
解决:用 var z = x * y; 先声明再使用(该报错与修复的完整演示见《Apache Commons JEXL 3.7.0 常用 API 与表达式语法详解》脚本一节)。
Q4:javac 报 unmappable character (0xE5) for encoding US-ASCII 怎么办?
源码含中文而平台默认编码为 US-ASCII 所致,编译加 -encoding UTF-8(5.5 实测)。Maven 工程则在 pom 中设 project.build.sourceEncoding=UTF-8(4.3 已设置)。
Q5:源码构建报 Detected Maven Version: 3.6.3 is not in the allowed range [3.9,) 怎么办?
3.7.0 源码工程的 enforcer 强制 Maven ≥ 3.9(6.3 实录)。按 6.4 下载 apache-maven-3.9.9-bin.tar.gz 解压到 /opt,用 /opt/apache-maven-3.9.9/bin/mvn package -DskipTests 构建即可,无需改动系统自带 Maven。
十、卸载与清理
JEXL 不做系统级安装,删除各方案产生的目录与包即可完全清理:
rm -rf /opt/jexl-demo /opt/jexl3-dist /opt/jexl3-demo /opt/commons-jexl3-3.7.0-src /opt/apache-maven-3.9.9 /opt/commons-jexl-3.7.0-bin.tar.gz /opt/commons-jexl-3.7.0-src.tar.gz /opt/apache-maven-3.9.9-bin.tar.gz
ls -d /opt/jexl-demo /opt/jexl3-dist /opt/jexl3-demo /opt/commons-jexl3-3.7.0-src /opt/apache-maven-3.9.9 2>/dev/null || echo "jexl directories removed"
输出:
jexl directories removed
说明:上面一次性移除 Maven 工程、二进制包解压目录、验证程序目录、源码目录与 Maven 3.9.9;ls -d 在目录全部删除后走 || 分支输出确认信息。如需同时移除 JDK 与 Maven 系统包,可执行 apt-get remove -y openjdk-17-jdk maven;本地 Maven 仓库缓存 ~/.m2 需另行清理(rm -rf ~/.m2/repository),其中缓存了 commons-jexl3-3.7.0.jar 与 commons-logging-1.4.0.jar。
十一、参考链接
- Apache Commons JEXL 官方站点:https://commons.apache.org/proper/commons-jexl/
- 官方发行下载目录(二进制 binaries/、源码 source/):https://downloads.apache.org/commons/jexl/
- Maven 中央仓库坐标页:https://repo1.maven.org/maven2/org/apache/commons/commons-jexl3/
- Apache Commons JEXL 3.7.0 Javadoc(官方包内
apidocs/与 javadoc jar 同内容):https://commons.apache.org/proper/commons-jexl/apidocs/ - Apache Commons JEXL 用户指南:https://commons.apache.org/proper/commons-jexl/userguide.html