java利用JEXL实现动态表达式编译

 更新时间:2021年4月15日 10:02  点击:1751

背景

做项目突然遇到这样的需求:
系统要获取多个数据源的数据,并进行处理,最后输出多个字段。字段的计算规则一般是简单的取值最多加一点条件判断。
而且需要动态变动!!例如一个字段a的取值,如果a > 10的时候输出10,a <= 10则输出a。这里的10可能在一天后改成8,也可能在后天就改成了12。当然,如果只是一个数字的变动还好说,我们可以使用数据库进行存储。但是,万一哪天需求突然变成了a < 10的时候输出10,a >=10 则输出a,就需要对代码改动,再测试再发布才能到生产环境使用。
一两个这样的字段还没什么,如果整个系统所依赖的字段都有这样的属性,那么我们就需要找一种方法来实现动态的加载逻辑。
下面介绍的JEXL就可以解决这种问题

JEXL(Java Expression Language)介绍

JEXL – Apache Commons JEXL Overview

下面用一些实例来介绍JEXL的使用方法

实例

maven依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-jexl</artifactId>
    <version>2.0</version>
</dependency>

正则表达式匹配

首先写一个公共方法:

public class Util {
    public static boolean regMatch(String regEx, String str) {
        Pattern pattern = Pattern.compile(regEx);
        return pattern.matcher(str).matches();
    }
}

下面是使用JEXL调用的方法

public void RL() {
    JexlContext jc = new MapContext();
    String str = "一二三四五六七八九十";
    jc.set("Util", new Util());
    jc.set("str", str);
    jc.set("ans", "");
    String expression = "ans = Util.regMatch(\"[\u4e00-\u9fa5]{10,}\",str)";
    Expression e = new JexlEngine().createExpression(expression);
    e.evaluate(jc);
    System.out.println(jc.get("ans"));
}

代码中的expression变量就是可以动态编译的表达式,这里要注意表达式中出现的所有变量,都需要事先set进JexlContext中,否则会报错。这里有多种形式的错误:

①如果没有set”Util”,程序运行中会抛出异常。

org.apache.commons.jexl2.JexlException: TmpTest.RL@40![13,40]: 'ans = QeUtil.regMatch('[一-龥]{10,}', str);' attempting to call method on null

②如果没有set”str”,程序不会抛出异常,并输出null。如果你的regMatch方法中有判空处理,就会输出判空的结果。如果没有判空处理,在控制台的输出如下:

警告: TmpTest.RL@39![36,39]: 'ans = QeUtil.regMatch('[一-龥]{10,}', str);' undefined variable str
二月 21, 2017 4:00:41 下午 org.apache.commons.jexl2.JexlEngine invocationFailed
警告: TmpTest.RL@39![13,40]: 'ans = QeUtil.regMatch('[一-龥]{10,}', str);' method invocation error
java.lang.NullPointerException

③如果没有set”ans”,程序会正常运行,并输出正确值
为了保险起见,建议表达式中出现的所有变量,都需要事先set进JexlContext中

循环

JEXL支持两种循环方式:

for(item : list) {
    x = x + item;
}

while (x lt 10) {
    x = x + 2;
}

下面是使用while的实例:

public void loop() {
    JexlContext jc = new MapContext();
    jc.set("a", 1);
    jc.set("b", "0");
    jc.set("ans", new StringBuffer());
    Expression e = new JexlEngine().createExpression("while (a < 10) {a = a + 1;ans.append(b);}");
    e.evaluate(jc);
    System.out.println(jc.get("ans"));
}

get\set方法调用

JEXL支持传入对象,并调用对象的方法
下面的简单的get\set方法的实例:

public void getSet() {
    TmpTest tmpTest = new TmpTest();
    tmpTest.setA(1);
    JexlContext jc = new MapContext();
    jc.set("tmpTest", tmpTest);
    jc.set("ans", "");
    Expression e = new JexlEngine().createExpression("ans = tmpTest.getA()");
    e.evaluate(jc);
    System.out.println(jc.get("ans"));
    e = new JexlEngine().createExpression("ans = tmpTest.setA(2)");
    e.evaluate(jc);
    TmpTest tmpTest1 = (TmpTest) jc.get("tmpTest");
    System.out.println(tmpTest1.getA());
}

上面的用例会在控制台先输出1,再输出2

下面是其他网友的评论

ScriptEngine比这个方便n倍,能动态执行js function,了解一下

[!--infotagslink--]

相关文章

  • VSCode C++多文件编译的简单使用方法

    这篇文章主要介绍了VSCode C++多文件编译的简单使用方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
  • Vue3 编译流程-源码解析

    今天将从 Vue 的入口文件开始,看看声明了一个 Vue 的单文件之后是如何被 compile-core 编译核心模块编译成渲染函数的。下面小编讲解并附上代码分析展现在文章里,感兴趣的小伙伴不要错过奥...2021-09-25
  • vscode和cmake编译多个C++文件的实现方法

    这篇文章主要介绍了vscode和cmake编译多个C++文件的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-10
  • 使用 C# 动态编译代码和执行的代码

    一个控制台例子, 演示了如何用 C# 动态的生成代码, 编译代码, 最后执行...2020-06-25
  • jsp 自动编译机制详细介绍

    这篇文章主要介绍了 Jasper的自动检测实现的机制比较简单,依靠某后台线程不断检测JSP文件与编译后的class文件的最后修改时间是否相同,若相同则认为没有改动,但倘若不同则需要重新编译,需要的朋友可以参考下...2016-12-02
  • QT5编译使用QFtp的方法步骤

    这篇文章主要介绍了QT5编译使用QFtp的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-04
  • Windows系统下Eclipse搭建ESP32编译环境及安装过程

    Ecppse 使用了 ESP-IDF 中的 Makefile 支持。这意味着您需要从创建 ESP-IDF 项目开始。您可以使用 github 中的 idf-template 项目,接下来通过本文给大家介绍Windows系统下Eclipse搭建ESP32编译环境及安装过程,感兴趣的朋友一起看看吧...2021-10-18
  • 手把手教你如何编译打包video.js

    这篇文章主要介绍了编译打包video.js的方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧...2020-12-09
  • 解析C语言与C++的编译模型

    C++继承了C的编译模型,C语言的编译链接模型相对简洁,但C++继承了这些机制之后变得更加复杂难以理解,这里就来带大家简要解析C语言与C++的编译模型...2020-04-25
  • Shell脚本构建Docker 半自动化编译打包发布应用操作

    这篇文章主要介绍了Shell脚本构建Docker 半自动化编译打包发布应用操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-30
  • 基于C++自动化编译工具的使用详解

    本篇文章是对C++中自动化编译工具的使用进行了详细的分析介绍,需要的朋友参考下...2020-04-25
  • 解决php编译中遇到各种error办法

    php编译安装碰到各种的问题了,我们这里整理了一篇关于解决php编译中遇到各种error办法的文章,希望此文章能够为各位带来帮助呀。 1) Configure: error: xml2-config...2016-11-25
  • go select编译期的优化处理逻辑使用场景分析

    select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。接下来通过本文给大家介绍go select编译期的优化处理逻辑使用场景分析,感兴趣的朋友一起看看吧...2021-06-28
  • C/C++编译器GCC下的常用编译命令总结

    这篇文章主要介绍了C/C++编译器GCC下的常用编译命令总结,本文的示例环境为Linux系统,需要的朋友可以参考下...2020-04-25
  • 编译安装redisd的方法示例详解

    这篇文章主要介绍了编译安装redisd的方法示例详解,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-15
  • .NET的动态编译与WS服务调用详解

    这篇文章介绍了.NET的动态编译与WS服务调用详解,有需要的朋友可以参考一下,希望对你有所帮助...2021-09-22
  • C#动态编译并执行字符串样例

    这篇文章主要为大家详细介绍了C#动态编译并执行字符串样例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
  • .NET Core 源码编译的问题解析

    这篇文章主要介绍了.NET Core 源码编译的问题解析,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06
  • .NET Core单文件发布静态编译AOT CoreRT的方法详解

    这篇文章主要给大家介绍了关于.NET Core单文件发布静态编译AOT CoreRT的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • strings命令分析浅谈Go和C++编译时的一点小区别

    今天小编就为大家分享一篇关于strings命令分析浅谈Go和C++编译时的一点小区别,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2020-04-25