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() {
- //都为数字,结果为:true
- String 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);
- //有一个为数字,都会转成数字比较;结果为:true
- expression = "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);
- //都为字符串,结果为:false
- expression = "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;
- @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();
- }
- @Test
- public void test01() {
- //都为数字,结果为:true
- String 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);
- //有一个为数字,都会转成数字比较;结果为:true
- expression = "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);
- //都为字符串,结果为:false
- expression = "money > '5000'";
- jexlExpression = engine.createExpression(expression);
- context = new MapContext();
- context.set("money", "10000");
- o = jexlExpression.evaluate(context);
- System.out.println(o);
- }
- @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
- }
- @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
- }
- @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
- }
- }
复制代码 JexlCase.java
参考:
https://commons.apache.org/proper/commons-jexl/
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |