利用 JEXL 动态计算表达式

定义

Java Expression Language (JEXL) 是一个表达式语言引擎,可以用来校验数据。

背景

项目中,需要配置一些规则,然后根据动态的数据去验证规则。比如配置如下的规则:

人数大于5,并且名称包含 “test” 才能验证通过。

按照传统的思路,一般就是写死代码,如:

1
2
3
if(num > 5 && name.contains("test")){
return true;
}

但是,如果规则变成了人数等于6,那就要去修改代码(虽然值可以动态获取,但是 > 不能动态改为 = ),比较麻烦。

那么就换一种思路,做成可以配置的,按照上面的规则,就是配置成:

纬度1 公式1 值1 && 纬度2 公式2 值2

把纬度、公式用数据字典进行替换,上图的配置内容存到库中结果就是:

${num} > 5 && ${name}.contains(“test”)

( PS :可能有人会有疑问,contains 需要引号和括号,怎么进行替换呢,我这边是这样处理的,公式实际存的是 > ${exp}.contains("${exp}") ,这时候只要替换掉 ${exp} 就行了。)

最后要验证的时候,就用实际值替换${num}${name}就得到如下的表达式:

10 > 5 && “test jexl”.contains(“test”)

那么问题来了,怎么动态验证这个表达式的结果呢?

JEXL 使用示例

为了计算上面表达式的结果,这时候就需要用到 JEXL 了,我们直接看代码。

首先是 pom.xml,我这边用的是 2.1.1 版本,3 版本代码有一点区别,有兴趣的可以自己研究下。

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl</artifactId>
<version>2.1.1</version>
</dependency>

直接上代码:

1
2
3
4
JexlContext jc = new MapContext();
Expression e = new JexlEngine().createExpression("10 > 5 && \"test jexl\".contains(\"test\")");
Object result = e.evaluate(jc);
System.out.println(result);

由于 createExpression() 需要传一个 string 参数,所以表达式中的引号需要转义下。

运行结果为:true

如果把表达式改为 :

1 > 5 && “test jexl”.contains(\”test\”)

运行结果则为:false

JEXL 设置参数

JEXL 也可以通过设置参数来计算结果,比如公式为:

x - y

那么我们可以通过 JEXL 来设置 x 和 y 的值。

1
2
3
4
5
6
JexlContext jc = new MapContext();
jc.set("x", 10);
jc.set("y", 20);
Expression e = new JexlEngine().createExpression("x - y");
Object result = e.evaluate(jc);
System.out.println(result);

运行结果为:-10

这边有个容易出错的地方,jc.set()第二个参数为 object 类型,需要根据实际类型来传值(比如值为 string 的时候需要带引号),否则可能会报错。

总结

JEXL 一般被用来验证数据,以上只介绍了 JEXL 的一些基本功能,JEXL 的其它功能还有很多,有兴趣的可以深入研究。