Ubuntu 22.04 安装 Apache Commons JEXL 指南

在 Ubuntu 22.04 上从零引入 Apache Commons JEXL 3.7.0,覆盖 JDK 17 与 Maven 准备、Maven 依赖、官方二进制包离线安装与源码编译三套方案,附真实回显与 3.7.0 行为变化排障。

Apache Commons JexlApache Commons Jexl2026/10/14

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。

二、前置条件

在开始安装前,请确认以下条件满足,否则后续步骤会因权限或网络问题中断。

  1. root 权限或 sudo:安装 JDK、Maven 与写入 /opt 目录需要相应权限。
  2. 网络可达:需要访问 downloads.apache.org(官方二进制/源码包)与 repo1.maven.org(Maven 中央仓库);网络连通性将在 4.3 的依赖下载与 5.2、6.2 的官方包下载时随实际命令自然验证。
  3. 磁盘空间:官方二进制包与源码包均为 MB 级,加上本地 Maven 仓库缓存建议预留 ≥ 200 MB。
  4. 纯 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 jarenforcer 校验后 Maven 构建
依赖完整性自动含 commons-logging需手动补 1 个 jar自动解析
Maven 版本要求3.6.3 实测可用无强制 ≥ 3.9(实测 3.6.3 被拒)
耗时秒级分钟内最长(实测约 2 分 16 秒)
额外产物—javadoc/sources/apidocsjar + 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。

十一、参考链接

  1. Apache Commons JEXL 官方站点:https://commons.apache.org/proper/commons-jexl/
  2. 官方发行下载目录(二进制 binaries/、源码 source/):https://downloads.apache.org/commons/jexl/
  3. Maven 中央仓库坐标页:https://repo1.maven.org/maven2/org/apache/commons/commons-jexl3/
  4. Apache Commons JEXL 3.7.0 Javadoc(官方包内 apidocs/ 与 javadoc jar 同内容):https://commons.apache.org/proper/commons-jexl/apidocs/
  5. Apache Commons JEXL 用户指南:https://commons.apache.org/proper/commons-jexl/userguide.html