JEXL 入门实战

news/2025/2/23 22:45:08/文章来源:https://www.cnblogs.com/wuyongyin/p/18576921

JEXL 是一个在 Java 中实现动态表达式和脚本功能的库,本文主要介绍其基本概率和使用。

1、背景

在软件开发中,动态执行表达式是一项非常重要的能力。特别是在动态规则处理、配置文件解析以及灵活的业务逻辑实现等场景下,使用一种能够在运行时解析和执行表达式的工具显得尤为重要。JEXL(Java Expression Language)就是这样一种工具,它为 Java 开发者提供了强大的表达式和脚本处理能力。

2、简介

JEXL 实现了一种表达式语言,基于对 JSTL 表达式语言的一些扩展,支持在 shell 或 ECMAScript 中看到的大多数使用方法。
它的目标是向技术人员暴露可用于企业平台的脚本功能。在许多使用场景中,JEXL 允许应用程序的最终用户编写自己的脚本或表达式,并确保它们在受控的功能约束内执行。

该库提供了一个小巧的 API——核心功能仅包含 3 个类和 10 个方法——可以在多种场景下使用:

  • 脚本功能    允许用户评估或定义一些简单的表达式,如计算公式。
  • 模块或组件配置    A、在配置文件中使用变量和表达式  B、使用 IOC 很方便,但总体复杂性不需要(或不能依赖于)一个成熟的库(Spring、Guice…)
  • 接口和实现松耦合    A、有一些可选类不能作为编译时的依赖项  B、必须集成并调用“遗留”代码或者使用不想强烈依赖的组件
  • 简单的模板功能    应用有基本的模板需求,而 JSP 或 Velocity 可能会显得过于复杂或部署不便

JEXL 的名称代表 Java 表达式语言(Java EXpression Language),是一种简单的表达式语言,最初受到 Apache Velocity 和 JavaServer Pages 标准标签库(JSTL)1.1 版以及 JavaServer Pages 2.0 版(JSP)中定义的表达式语言的启发。受 Unified EL 启发,JEXL 2.0 引入了许多特性。其语法现在接近 ECMAScript 和“shell 脚本”的混合,便于技术人员掌握。

JEXL的 API 和表达式语言通过反射暴露对象属性的 getter 和 setter 方法。它还将公共类字段视为属性,并允许调用任何可访问的方法。

3、使用

3.1、引入依赖

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-jexl3</artifactId><version>3.4.0</version>
</dependency>

3.2、创建 JexlEngine

@Before
public void before() {JexlFeatures features = new JexlFeatures().loops(true) //是否允许使用循环语句,如:for、while.sideEffectGlobal(true) //是否允许修改全局变量.sideEffect(true); //是否允许修改变量engine = new JexlBuilder().features(features).strict(true).permissions(JexlPermissions.RESTRICTED).create();
}

3.3、表达式

createExpression 方法用于创建单个表达式,计算并返回一个值。

3.3.1、简单表达式

@Test
public void test01() {//都为数字,结果为:trueString expression = "money > 5000";JexlExpression jexlExpression = engine.createExpression(expression);JexlContext context = new MapContext();context.set("money", 10000);Object o = jexlExpression.evaluate(context);System.out.println(o);//有一个为数字,都会转成数字比较;结果为:trueexpression = "money > '5000'";jexlExpression = engine.createExpression(expression);context = new MapContext();context.set("money", 10000);o = jexlExpression.evaluate(context);System.out.println(o);expression = "money > 5000";jexlExpression = engine.createExpression(expression);context = new MapContext();context.set("money", "10000");o = jexlExpression.evaluate(context);System.out.println(o);//都为字符串,结果为:falseexpression = "money > '5000'";jexlExpression = engine.createExpression(expression);context = new MapContext();context.set("money", "10000");o = jexlExpression.evaluate(context);System.out.println(o);
}

3.3.2、方法使用

@Test
public void test02() {String expression = "Math.max(a, b)";JexlExpression jexlExpression = engine.createExpression(expression);JexlContext context = new MapContext();context.set("Math", Math.class);context.set("a", 6);context.set("b", 7);Object o = jexlExpression.evaluate(context);System.out.println(o); //7
expression = "list.contains('a')";jexlExpression = engine.createExpression(expression);context = new MapContext();List<String> list = new ArrayList<>();list.add("a");context.set("list", list);o = jexlExpression.evaluate(context);System.out.println(o); //true
}

或:

@Test
public void test02_2() {Map<String, Object> map = new HashMap<>();map.put("Math", Math.class);List<String> list = new ArrayList<>();list.add("a");map.put("list", list);JexlEngine jexlEngine = new JexlBuilder().namespaces(map).create();String expression = "Math:max(a, b)";JexlExpression jexlExpression = jexlEngine.createExpression(expression);JexlContext context = new MapContext();context.set("a", 6);context.set("b", 7);Object o = jexlExpression.evaluate(context);System.out.println(o); //7
expression = "list:contains('a')";jexlExpression = jexlEngine.createExpression(expression);context = new MapContext();o = jexlExpression.evaluate(context);System.out.println(o); //true
}

3.3.3、数组操作

@Test
public void test03() {String expression = "text =~ [1, 2, '吧啦吧啦']"; //是否属性数组中的一个JexlExpression jexlExpression = engine.createExpression(expression);JexlContext context = new MapContext();context.set("text", "1");Object evaluate = jexlExpression.evaluate(context);System.out.println(evaluate); //false
context.set("text", 1);evaluate = jexlExpression.evaluate(context);System.out.println(evaluate); //true//不属于数组中的任意一个expression = "text !~ [1, 2, '吧啦吧啦']";jexlExpression = engine.createExpression(expression);context.set("text", "2");evaluate = jexlExpression.evaluate(context);System.out.println(evaluate); //true
context.set("text", 2);evaluate = jexlExpression.evaluate(context);System.out.println(evaluate); //false
}

3.4、脚本

createScript 方法用于创建多个语句的脚本,可能包含复杂的控制流或多个计算,返回最终的执行结果;当然 createScript 方法也可用于表达式的计算,但是 createExpression 方法不能用于脚本的计算。

@Test
public void test04() {String script = "money > 5000";JexlScript jexlScript = engine.createScript(script);JexlContext context = new MapContext();context.set("money", 10000);Object o = jexlScript.execute(context);System.out.println(o); //true
script = "for (item : list) { total += item;}\n return 10;";jexlScript = engine.createScript(script);context = new MapContext();context.set("list", Arrays.asList(1, 2, 3, 4, 5));context.set("total", 0);Object executeResult = jexlScript.execute(context);log.info("executeResult={},total={}", executeResult, context.get("total")); //10,15
script = "for (item : list) { total += item; }\n return 10;";jexlScript = engine.createScript(script, "list", "total");context = new MapContext();executeResult = jexlScript.execute(context, Arrays.asList(1, 2, 3, 4, 5), 0);log.info("executeResult={}", executeResult); //10
}

3.5、完整代码

package com.abc.demo.jexl;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.jexl3.*;
import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.junit.Before;
import org.junit.Test;import java.util.*;@Slf4j
public class JexlCase {private JexlEngine engine;@Beforepublic void before() {JexlFeatures features = new JexlFeatures().loops(true) //是否允许使用循环语句,如:for、while.sideEffectGlobal(true) //是否允许修改全局变量.sideEffect(true); //是否允许修改变量engine = new JexlBuilder().features(features).strict(true).permissions(JexlPermissions.RESTRICTED).create();}@Testpublic void test01() {//都为数字,结果为:trueString expression = "money > 5000";JexlExpression jexlExpression = engine.createExpression(expression);JexlContext context = new MapContext();context.set("money", 10000);Object o = jexlExpression.evaluate(context);System.out.println(o);//有一个为数字,都会转成数字比较;结果为:trueexpression = "money > '5000'";jexlExpression = engine.createExpression(expression);context = new MapContext();context.set("money", 10000);o = jexlExpression.evaluate(context);System.out.println(o);expression = "money > 5000";jexlExpression = engine.createExpression(expression);context = new MapContext();context.set("money", "10000");o = jexlExpression.evaluate(context);System.out.println(o);//都为字符串,结果为:falseexpression = "money > '5000'";jexlExpression = engine.createExpression(expression);context = new MapContext();context.set("money", "10000");o = jexlExpression.evaluate(context);System.out.println(o);}@Testpublic void test02() {String expression = "Math.max(a, b)";JexlExpression jexlExpression = engine.createExpression(expression);JexlContext context = new MapContext();context.set("Math", Math.class);context.set("a", 6);context.set("b", 7);Object o = jexlExpression.evaluate(context);System.out.println(o); //7
expression = "list.contains('a')";jexlExpression = engine.createExpression(expression);context = new MapContext();List<String> list = new ArrayList<>();list.add("a");context.set("list", list);o = jexlExpression.evaluate(context);System.out.println(o); //true
    }@Testpublic void test02_2() {Map<String, Object> map = new HashMap<>();map.put("Math", Math.class);List<String> list = new ArrayList<>();list.add("a");map.put("list", list);JexlEngine jexlEngine = new JexlBuilder().namespaces(map).create();String expression = "Math:max(a, b)";JexlExpression jexlExpression = jexlEngine.createExpression(expression);JexlContext context = new MapContext();context.set("a", 6);context.set("b", 7);Object o = jexlExpression.evaluate(context);System.out.println(o); //7
expression = "list:contains('a')";jexlExpression = jexlEngine.createExpression(expression);context = new MapContext();o = jexlExpression.evaluate(context);System.out.println(o); //true
    }@Testpublic void test03() {String expression = "text =~ [1, 2, '吧啦吧啦']"; //是否属性数组中的一个JexlExpression jexlExpression = engine.createExpression(expression);JexlContext context = new MapContext();context.set("text", "1");Object evaluate = jexlExpression.evaluate(context);System.out.println(evaluate); //false
context.set("text", 1);evaluate = jexlExpression.evaluate(context);System.out.println(evaluate); //true//不属于数组中的任意一个expression = "text !~ [1, 2, '吧啦吧啦']";jexlExpression = engine.createExpression(expression);context.set("text", "2");evaluate = jexlExpression.evaluate(context);System.out.println(evaluate); //true
context.set("text", 2);evaluate = jexlExpression.evaluate(context);System.out.println(evaluate); //false
    }@Testpublic void test04() {String script = "money > 5000";JexlScript jexlScript = engine.createScript(script);JexlContext context = new MapContext();context.set("money", 10000);Object o = jexlScript.execute(context);System.out.println(o); //true
script = "for (item : list) { total += item;}\n return 10;";jexlScript = engine.createScript(script);context = new MapContext();context.set("list", Arrays.asList(1, 2, 3, 4, 5));context.set("total", 0);Object executeResult = jexlScript.execute(context);log.info("executeResult={},total={}", executeResult, context.get("total")); //10,15
script = "for (item : list) { total += item; }\n return 10;";jexlScript = engine.createScript(script, "list", "total");context = new MapContext();executeResult = jexlScript.execute(context, Arrays.asList(1, 2, 3, 4, 5), 0);log.info("executeResult={}", executeResult); //10
    }
}
JexlCase.java

 

 

参考:
https://commons.apache.org/proper/commons-jexl/

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/888606.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Octopus游戏服务器框架

Octopus一个跨平台的lua游戏服务器开发框架,该框架代码采用C++编写,开发者只需要调用SDK提供的接口函数和绑定对应的回调函数即可。框架在逻辑层表现为单线程的开发模式,使开发者更加易使用,易调试,易维护,易扩展,同时确保有快速的响应能力。 框架使用面向对象的开发模式…

【蓝牙小程序】实现简易table表格

Demo效果图 正文 由于需要开发小程序,前端又是自己弄,类似table的标签也没有,后来看到小程序文档中推荐使用flex布局,就把css中的flex布局学了一遍,效果还行,大家将就看一下table.wxml <view class="table"><view class="tr bg-w"><…

工程管理(二)

工程模板介绍 DevEco Studio支持多种品类的应用/元服务开发,预置丰富的工程模板,可以根据工程向导轻松创建适应于各类设备的工程,并自动生成对应的代码和资源模板。同时,DevEco Studio还提供了多种编程语言供开发者进行应用/元服务开发,包括ArkTS、JS和C/C++。工程模板支持…

工程管理(一)

APP包结构 在进行应用/元服务开发前,开发者应该掌握应用/元服务的逻辑结构。 应用/元服务发布形态为APP Pack(Application Package),它是由一个或多个HAP(Harmony Ability Package)包以及描述APP Pack属性的pack.info文件组成。 一个HAP在工程目录中对应一个Module,它是…

包和抽象类介绍--java进阶day02

1.package包导包第二点需要注意 a包和b包都存有Student类,c包存有测试类,我们在c中创建Student对象,系统会询问你要哪个包的Student类,并自动帮你导包.在导完a包的学生类后,想要再次导入b包的学生类就不能再像之前那样导了全类名导包 通过带包名将b包重复的学生类导入2.抽…

2025.2.23(二进制等等)

平常我们生活使用的是十进制,在计算机中常用二进制等。 二进制是用0,1表示,逢二进1. 啊啊啊好难表达。 例如2在二进制中为10.哎上图片。。。。除2取余法,哎呀,不管了看图

Deveco Studio下载

Deveco Studio最新版本-下载中心根据自己的操作系统下载合适的版本即可 Windows环境 运行环境要求 为保证DevEco Studio正常运行,建议电脑配置满足如下要求:操作系统:Windows10 64位、Windows11 64位 内存:16GB及以上 硬盘:100GB及以上 分辨率:1280*800像素及以上安装Dev…

第一次作业—软件二次开发

一.项目来源 本次作业的项目来源是https://blog.csdn.net/m0_65636467/article/details/128069045?sharetype=blog&shareId=128069045&sharerefer=APP&sharesource=2301_80676751&sharefrom=link中的第7个C语言超市收款系统 二.运行环境和运行结果 1.运行环境…

《痞子衡嵌入式半月刊》 第 118 期

痞子衡嵌入式半月刊: 第 118 期这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿。 上期回顾 :《…

【牛客训练记录】牛客周赛 Round 82

训练情况赛后反思 C题没想明白,但是发现了数列一定是不增加的,另外第一次出现的数字,那个位置就必须是那个数字,剩下可能是乘法原理之类的东西吧,但是没做出来 A题 判断字符串第一位和最后一位是否一致即可点击查看代码 #include <bits/stdc++.h> // #define int lo…

【Atcoder训练记录】AtCoder Beginner Contest 394

训练情况赛后反思 没在赛时打的,只做了签到TAT A题 统计字符串中 2 的数量,最后去掉其他的,只输出 2点击查看代码 #include <bits/stdc++.h> // #define int long long #define endl \nusing namespace std;void solve(){string s; cin>>s;int ans = 0;for(int …

DPDK收发包梳理

DPDKeal初始化 内存管理:大页,内存池 驱动开启调试信息 make config T=x86_64-native-linuxapp-gcc export EXTRA_CFLAGS=-O0 -g3 -ggdb make -j8 dpdk通过makefile编译 meson + ninja没学过,太麻烦了,可以参考dpdk17的文档,里面有介绍make编译方式。 https://doc.dpdk.or…