CentOS 8 安装 Apache Commons JEXL 指南

在 CentOS 8 上从零引入 Apache Commons JEXL 3.7.0,覆盖 JDK 8/17 并存的 JAVA_HOME 切换、Maven 依赖、官方二进制包离线安装与源码编译三套方案,附真实回显与 3.7.0 行为变化排障。

Apache Commons JexlApache Commons Jexl2026/10/14

CentOS 8 安装 Apache Commons JEXL 指南

目标:在 CentOS 8 上从零搭好 Apache Commons JEXL 3.7.0 的使用环境,完成 JDK 8/17 并存下的 JAVA_HOME 切换、Maven 依赖引入、官方二进制包离线安装与源码编译三种获取方式,并跑通真实表达式求值验证。以下全部命令与回显均已在 CentOS 8(x86_64)实测通过。

一、环境信息

本节先固定本次实测所用软硬件基线,便于读者逐项对照复现。

  • 操作系统:CentOS 8.4(x86_64)
  • JDK:系统默认 java 为 1.8.0_312,同时安装有 java-17-openjdk-devel(OpenJDK 17.0.1 LTS),二者并存
  • 构建工具:Apache Maven 3.5.4(Red Hat 构建版 3.5.4-5,系统仓库默认版本)
  • 目标库: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
echo ===
mvn -v | head -2

输出:

openjdk version "1.8.0_312"
OpenJDK Runtime Environment (build 1.8.0_312-b07)
OpenJDK 64-Bit Server VM (build 1.8.0_312-b07, mixed mode)
===
Apache Maven 3.5.4 (Red Hat 3.5.4-5)
Maven home: /usr/share/maven

关键点:默认 java 是 1.8.0_312——CentOS 8 常见的多 JDK 并存形态。JEXL 3.7.0 示例工程按 release 17 编译,因此后续所有 mvn、javac、java 命令前都必须把 JAVA_HOME 指向 JDK 17(见 4.2),否则会因字节码版本不匹配而失败。

确认 JDK 17 在位并验证其版本:

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
$JAVA_HOME/bin/java -version 2>&1

输出:

openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment 21.9 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM 21.9 (build 17.0.1+12-LTS, mixed mode, sharing)

验证点:/usr/lib/jvm/java-17-openjdk 为系统软链路径,指向的 JDK 17.0.1 LTS 可用,本文全程使用该 JAVA_HOME。

二、前置条件

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

  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. 多 JDK 并存(CentOS 8 关键项):默认 java(1.8.0_312)无法编译/运行 release 17 工程,务必按 4.2 设置 JAVA_HOME 指向 JDK 17 后再执行任何构建命令。
  5. 纯 Java 库:JEXL 无本地原生依赖,x86_64 之外的架构同样可用。

⚠️ 注意:若内网无法直连上述域名,需按企业代理配置放行,否则 Maven 依赖与官方包均无法获取。本文实测环境为直连。

三、版本选型说明

Apache Commons JEXL(Java Expression Language)是 Apache 基金会的 Java 表达式引擎:在运行期把表达式字符串(如 1 + 2 * 3、age > 18 ? 'adult' : 'minor')解析为语法树并求值,常用于规则引擎、动态告警条件、工作流条件分支与文本模板渲染。

💡 技术拓展: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,低版本构建被拒(CentOS 8 系统自带 Maven 3.5.4 亦不满足)

💡 说明:这些变化点的真实报错回显会在本文第四、六、九章与《Apache Commons JEXL 3.7.0 配置参数详解》《Apache Commons JEXL 3.7.0 常用 API 与表达式语法详解》两篇中逐条出现,遇到对应异常时可直接对照排查。

JEXL 的 Maven 坐标为 org.apache.commons:commons-jexl3,groupId 固定 org.apache.commons。其依赖解析结果(实测 mvn dependency:tree,输出节选):

[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(见 FAQ Q1)。

官方发行包位于 downloads.apache.org 的 binaries/(二进制)与 source/(源码)子目录,本文分别在方案 B、C 中实测下载。

四、在线安装方案(A:Maven 依赖引入)

官方推荐方式:通过 Maven 引入 commons-jexl3:3.7.0,由构建工具自动解析传递依赖。适合可联网的开发/构建环境。

4.1 方案说明与适用场景

本方案先确认 JDK 8/17 并存环境下的 JAVA_HOME 切换,再创建一个最小 Maven 工程引入 commons-jexl3:3.7.0,编译并运行一个真实表达式求值程序完成验证。适合持续集成、可访问中央仓库的场景。

4.2 切换 Maven 使用 JDK 17

CentOS 8 的系统 Maven 3.5.4 通过 JAVA_HOME 决定所用的 JDK。JEXL 3.7.0 示例按 release 17 编译,先执行切换并验证(第一章已实测):

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
$JAVA_HOME/bin/java -version 2>&1

输出:

openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment 21.9 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM 21.9 (build 17.0.1+12-LTS, mixed mode, sharing)

验证点:$JAVA_HOME/bin/java -version 显示 17.0.1,说明 JDK 17 可用;该 export 对当前会话生效,后续 mvn 命令都会读取 JAVA_HOME。持久化方法见第八章。

⚠️ 注意:若系统尚未安装 JDK 17 与 Maven,可执行 dnf install -y java-17-openjdk-devel maven 安装;本文实测环境两者均已就绪,安装后以上述验证命令与 mvn -v | head -2 的回显为准。

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-centos-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:要求 Maven 必须运行在 JDK 17 上(见 4.2),用 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/Main.java 全文:

package demo;

import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.MapContext;

/** CentOS 8 + JDK17 最小求值验证 */
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", "centos8");
        ctx.set("age", 20);
        System.out.println(jexl.createExpression("'Hello, ' + name + '!'").evaluate(ctx));
        System.out.println(jexl.createExpression("age > 18 ? 'adult' : 'minor'").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 Q4)。

4.5 编译与运行验证

编译与 classpath 生成(-q 静默模式下编译成功时无输出):

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
cd /opt/jexl-demo
mvn -q compile
mvn -q dependency:build-classpath -Dmdep.outputFile=cp.txt
$JAVA_HOME/bin/java -Dfile.encoding=UTF-8 -cp "target/classes:$(cat cp.txt)" demo.Main

输出:

JEXL impl version = 3.7.0
1 + 2 * 3 = 7
Hello, centos8!
adult
55

验证点:JEXL impl version = 3.7.0 由 jar 内置清单读出,直接证明运行时加载的是 3.7.0;1 + 2 * 3 = 7 与 Hello, centos8! 验证表达式求值与变量注入;adult 验证三元与比较(age=20 > 18);55 为 1 到 10 求和的脚本结果,验证多语句脚本与 var 声明。Maven 3.5.4 + JDK 17 编译通过,说明方案 A 在 CentOS 8 的系统 Maven 上可用。

⚠️ 注意:运行用的是 $JAVA_HOME/bin/java 而非系统 java——后者是 1.8.0_312,无法加载 release 17 字节码。所有编译与运行命令都必须在 JAVA_HOME 指向 JDK 17 的前提下执行。

五、离线安装方案(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.5 节 cp.txt 生成的 classpath 即含该路径);若本机从未执行过方案 A,从中央仓库取同一坐标(commons-logging:commons-logging:1.4.0)的 jar 放入任意目录并加入 classpath 即可,用 ls -l 确认文件在位后进入 5.5。

5.4 编写最小验证程序

/opt/jexl3-demo/Main.java 全文(与 4.4 同源,仅验证官方二进制包可用,无需 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 内置的实现版本号,再依次验证纯表达式、带上下文变量的插值表达式与多语句脚本。此文件无 package 声明,编译后直接位于当前目录运行。

5.5 编译与运行

含中文注释的源码在 locale 为 POSIX/US-ASCII 类取值时编译会报 unmappable character(实测报错节选,共 36 处同类错误):

Main.java:5: error: unmappable character (0xE5) for encoding US-ASCII
/** ???????????????????????????????????? */
    ^
Main.java:5: error: unmappable character (0xAE) for encoding US-ASCII
     ^
...
36 errors

解决:编译显式加 -encoding UTF-8,运行统一用 $JAVA_HOME/bin/java(指向 JDK 17):

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
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 直接证明运行时加载的是 3.7.0;1 + 2 * 3 = 7 与 Hello, bin-dist-user! 验证表达式求值;55 验证多语句脚本。classpath 中 .(当前目录下的 Main.class)+ JEXL jar + commons-logging jar 三者缺一不可。

⚠️ 注意:CentOS 8 上系统默认 java 为 1.8.0_312,javac/java 都建议改用 $JAVA_HOME/bin/ 下的 17 版本;同时 -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,而 CentOS 8 系统自带的 Maven 只有 3.5.4,不满足要求,因此本章必须先安装 Maven 3.9.9;JDK 侧继续使用 JAVA_HOME 指向的 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 安装 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 满足 enforcer 要求的 [3.9,) 区间;该安装为目录级,不覆盖系统 /usr/share/maven 的 3.5.4,两者可并存。

6.4 enforcer 版本校验说明

构建入口 pom.xml 由 maven-enforcer-plugin 的 RequireMavenVersion 规则校验 Maven 版本,允许区间为 [3.9,)。实测在 Maven 3.6.3 的环境中直接构建被拒(真实报错,CentOS 8 系统自带的 3.5.4 同样落在区间之外、会被同一条规则拒绝):

[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,).

解读:这不是环境故障,而是 3.7.0 源码工程的硬性构建要求。按 6.3 装好 Maven 3.9.9 后用其构建即可。

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.5.4 实测可用无强制 ≥ 3.9(3.5.4 不满足,需另装 3.9.9)
JDK 要求JAVA_HOME 指向 JDK 17用 $JAVA_HOME/bin/javac、javaJAVA_HOME 指向 JDK 17
耗时分钟内分钟内最长(实测约 2 分 16 秒)
额外产物—javadoc/sources/apidocsjar + sources + tests + SBOM
适用场景CI / 可联网开发内网隔离、离线运行源码定制、调试、跟版本线

选择建议:可联网优先方案 A(最简单、传递依赖自动解析,系统 Maven 3.5.4 实测可编译引用工程);内网隔离选方案 B(一次下载 + 一个依赖 jar 即可离线运行);需要改源码或跟随新版本选方案 C(必须先备好 Maven ≥ 3.9)。三种方式产出的都是同一个 3.7.0 运行时,行为一致,可按场景互换。

八、配置说明

JEXL 是进程内库,无独立配置文件;CentOS 8 上需要关注的「配置」集中在 JDK 切换、Maven 与运行期编码,本节逐项说明。

  • JAVA_HOME(CentOS 8 关键项):系统默认 java 为 1.8.0_312,所有编译/运行命令前须 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk(4.2/5.5 实测写法)。该 export 仅对当前会话生效,持久化方法是写入 /etc/profile.d/java17.sh:
# /etc/profile.d/java17.sh 内容如下,新会话登录后自动生效
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk

配置说明:/etc/profile.d/ 下的 .sh 文件会在登录 shell 初始化时被 source,写入后重新登录(或 source /etc/profile.d/java17.sh)即可让 mvn、javac、java 系列命令默认使用 JDK 17,无需每条命令前手动 export。

  • Maven 版本:引用 JEXL(方案 A)用系统自带 Maven 3.5.4 实测可用;仅方案 C 源码构建被 enforcer 强制要求 Maven ≥ 3.9,按 6.3 独立安装 3.9.9 后用全路径调用。
  • 源码编码: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 -q dependency:build-classpath -Dmdep.outputFile=cp.txt 生成完整 classpath 后以 $JAVA_HOME/bin/java -cp "target/classes:$(cat cp.txt)" 运行(见 4.5);二进制包方式把 commons-logging-1.4.0.jar 一并拼进 -cp(见 5.5)。

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 /opt/jexl3-dist/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:mvn compile 失败,提示字节码版本不匹配怎么办?

Maven 或 javac 运行在了系统默认的 JDK 8 上。先 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk,再用 $JAVA_HOME/bin/java -version 2>&1 确认输出为 openjdk version "17.0.1" 2021-10-19 LTS(4.2 实测回显)后重新编译。长期方案是把 JAVA_HOME 写入 /etc/profile.d/java17.sh(见第八章)。

Q4:脚本里直接写 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; 先声明再使用(4.4 的 Main.java 即按此写法;完整演示见《Apache Commons JEXL 3.7.0 常用 API 与表达式语法详解》脚本一节)。

Q5:源码构建报 RequireMavenVersion failed with message(版本不在 [3.9,) 区间)怎么办?

3.7.0 源码工程的 enforcer 强制 Maven ≥ 3.9(6.4 实录),CentOS 8 系统自带的 3.5.4 不满足。按 6.3 下载 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 系统包,可执行 dnf remove -y java-17-openjdk-devel maven;本地 Maven 仓库缓存 ~/.m2 需另行清理(rm -rf ~/.m2/repository),其中缓存了 commons-jexl3-3.7.0.jar 与 commons-logging-1.4.0.jar;若配置过 /etc/profile.d/java17.sh 也一并删除。

十一、参考链接

  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