欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

JEXL 入门实战,1、背景在软件开发中

来源: javaer 分享于  点击 39948 次 点评:200

JEXL 入门实战,1、背景在软件开发中


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/

 

相关栏目:

用户点评