一个轻量级的类java语法规则引擎,作为一个嵌入式规则引擎在业务系统中使用。让业务规则定义简便而不失灵活。让业务人员就可以定义业务规则。支持标准的JAVA语法,还可以支持自定义操作符号、操作符号重载、函数定义、宏定义、数据延迟加载等。
QLExpress在阿里集团内部有很强的影响力,被广泛应用在电商场景中,在业务灵活多变的场景下常常需要规则引擎的支持。
官方地址为:https://github.com/alibaba/QLExpress
项目已经给了很多的使用案例,但是如果不亲自运行一下,不容易体会到其用法与强大之处,本次主要给一个QLExpress的运行案例,可以真正的跑起来官方给的各种案例代码。
案例
说明:
/**
* 执行一段文本
* @param expressString 程序文本
* @param context 执行上下文,执行上下文在执行的过程中会产生中间数据,上下文不要重复使用
* @param errorList 输出的错误信息List
* @param isCache 是否使用Cache中的指令集
* @param isTrace 是否输出详细的执行指令信息
* @return
* @throws Exception
*/
public Object execute(String expressString, IExpressContext context,
List errorList, boolean isCache, boolean isTrace) throws Exception {
return this.execute(expressString, context, errorList, isCache, isTrace, null);
}
运行案例:
package me.aihe.demo.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import com.ql.util.express.Operator;
import java.util.HashMap;
public class QLExpressionDemo {
public static void main(String[] args) throws Exception {
ExpressRunner runner = new ExpressRunner();
// getStarted(runner);
// basicStatement(runner);
// collectionStatement(runner);
// objectStatement(runner);
// functionStatement(runner);
// macronStatement(runner);
// workWithJavaStatement(runner);
// extendOperatorStatement(runner);
customOperatorStatement(runner);
}
/**
* 自定义操作符的使用
* @param runner
*/
private static void customOperatorStatement(ExpressRunner runner) throws Exception {
class JoinOperator extends Operator {
public Object executeInner(Object[] list) throws Exception {
Object opdata1 = list[0];
Object opdata2 = list[1];
if(opdata1 instanceof java.util.List){
((java.util.List)opdata1).add(opdata2);
return opdata1;
}else{
java.util.List result = new java.util.ArrayList();
result.add(opdata1);
result.add(opdata2);
return result;
}
}
}
// 返回结果 [1, 2, 3]
//(1)addOperator
DefaultContext context = new DefaultContext();
runner.addOperator("join",new JoinOperator());
Object r = runner.execute("1 join 2 join 3", context, null, false, false);
System.out.println(r);
// (2) addFunction
// 返回结果:[1 , 2 ]
runner.addFunction("joinfunc",new JoinOperator());
r = runner.execute("joinfunc(1,2,3)", context, null, false, false);
System.out.println(r);
//(3)replaceOperator
// 返回结果 [1, 2, 3]
runner.replaceOperator("+",new JoinOperator());
r = runner.execute("1 + 2 + 3", context, null, false, false);
System.out.println(r);
}
/**
* 操作符处理,一般不太常用,但可以自定义一些操作符
* @param runner
*/
private static void extendOperatorStatement(ExpressRunner runner) throws Exception {
runner.addOperatorWithAlias("如果", "if",null);
runner.addOperatorWithAlias("则", "then",null);
runner.addOperatorWithAlias("否则", "else",null);
IExpressContext context =new DefaultContext();
context.put("语文", 88);
context.put("数学", 99);
context.put("英语", 95);
String exp = "如果 (语文+数学+英语>270) 则 {return 1;} 否则 {return 0;}";
// DefaultContext context = new DefaultContext();
Object result = runner.execute(exp, context, null, false, false, null);
System.out.println(result);
}
/**
* 将Java中已经写好的一些方法,绑定到我们自定义的变量上,在业务中最常用的部分。
* @param runner
*/
private static void workWithJavaStatement(ExpressRunner runner) throws Exception {
// 在使用的时候会创建对象
runner.addFunctionOfClassMethod("取绝对值", Math.class.getName(), "abs",
new String[] { "double" }, null);
// 对象已经存在,直接调用对象中的方法
runner.addFunctionOfServiceMethod("打印", System.out, "println",new String[] { "String" }, null);
String exp = "a=取绝对值(-100);打印(\"Hello World\");打印(a.toString())";
DefaultContext context = new DefaultContext<>();
runner.execute(exp, context,null,false,false);
System.out.println(context);
}
/**
* Macro定义, 即预先定义一些内容,在使用的时候直接替换Macro中的变量为上下文的内容
* @param runner
*/
private static void macronStatement(ExpressRunner runner) throws Exception {
runner.addMacro("计算平均成绩", "(语文+数学+英语)/3.0");
runner.addMacro("是否优秀", "计算平均成绩>90");
IExpressContext context =new DefaultContext();
context.put("语文", 88);
context.put("数学", 99);
context.put("英语", 95);
Object result = runner.execute("是否优秀", context, null, false, false);
System.out.println(result);
}
/**
* 自定义在QLexpress中的函数
* 一般语句的最后一句话是返回结果
* @param runner
*/
private static void functionStatement(ExpressRunner runner) throws Exception {
String functionStatement = "function add(int a,int b){\n" +
" return a+b;\n" +
"};\n" +
"\n" +
"function sub(int a,int b){\n" +
" return a - b;\n" +
"};\n" +
"\n" +
"a=10;\n" +
"add(a,4) + sub(a,9);";
Object result = runner.execute(functionStatement, new DefaultContext<>(), null, false, false);
// runner.execute(functionStatement, new DefaultContext<>(), null, true, false, 1000);
System.out.println(result);
}
/**
* 对Java对象的操作
* 系统自动会import java.lang.*,import java.util.*;
* @param runner
*/
private static void objectStatement(ExpressRunner runner) throws Exception {
// TradeEvent tradeEvent = new TradeEvent();
// tradeEvent.setPrice(20.0);
// tradeEvent.setName("购物");
// tradeEvent.setId(UUID.randomUUID().toString());//
//
String objectStatement = "import me.aihe.demo.trade.TradeEvent;\n" +
" tradeEvent = new TradeEvent();\n" +
" tradeEvent.setPrice(20.0);\n" +
" tradeEvent.id=UUID.randomUUID().toString();\n" +
" System.out.println(tradeEvent.getId());\n" +
" System.out.println(tradeEvent.price);";
runner.execute(objectStatement, new DefaultContext<>(), null, false, false);
}
/**
* 在定义集合,数组相关的操作,ql有自己的语法,不能简单的使用Java语法
* //java语法:使用泛型来提醒开发者检查类型
* keys = new ArrayList();
* deviceName2Value = new HashMap(7);
* String[] deviceNames = {"ng","si","umid","ut","mac","imsi","imei"};
* int[] mins = {5,30};
*
* //ql写法:
* keys = new ArrayList();
* deviceName2Value = new HashMap();
* deviceNames = ["ng","si","umid","ut","mac","imsi","imei"];
* mins = [5,30];
*
*
* //java语法:对象类型声明
* FocFulfillDecisionReqDTO reqDTO = param.getReqDTO();
* //ql写法:
* reqDTO = param.getReqDTO();
*
* //java语法:数组遍历
* for(GRCRouteLineResultDTO item : list) {
* }
* //ql写法:
* for(i=0;i defaultContext = new DefaultContext<>();
HashMap mapData = new HashMap(){{
put("a","hello");
put("b","world");
put("c","!@#$");
}};
defaultContext.put("map",mapData);
//ql不支持for(obj:list){}的语法,只能通过下标访问。
String mapTraverseStatement = " keySet = map.keySet();\n" +
" objArr = keySet.toArray();\n" +
" for (i=0;i context = new DefaultContext();
* String express = "abc = NewMap(1:1,2:2); return abc.get(1) + abc.get(2);";
* Object r = runner.execute(express, context, null, false, false);
* System.out.println(r);
* express = "abc = NewList(1,2,3); return abc.get(1)+abc.get(2)";
* r = runner.execute(express, context, null, false, false);
* System.out.println(r);
* express = "abc = [1,2,3]; return abc[1]+abc[2];";
* r = runner.execute(express, context, null, false, false);
* System.out.println(r);
*/
}
/**
* 语法基本说明:
* 不支持try{}catch{}
* 不支持java8的lambda表达式
* 不支持for循环集合操作for (GRCRouteLineResultDTO item : list)
* 弱类型语言,请不要定义类型声明
* 不要用Templete(Map之类的)
* array的声明不一样
* min,max,round,print,println,like,in 都是系统默认函数的关键字,请不要作为变量名
* @param runner
*/
private static void basicStatement(ExpressRunner runner) throws Exception {
DefaultContext defaultContext = new DefaultContext<>();
// defaultContext.put("n",10); //直接从Java中传递上下文等于在表达式中传递上下文
String loopStatement = "sum=0;n=10;" +
"for(i=0;ib?a:b;";
result = runner.execute(maxmiumStatement, defaultContext, null, false, false);
System.out.println(result);
}
// 入门展示express使用部分
private static void getStarted(ExpressRunner runner) throws Exception {
DefaultContext context = new DefaultContext();
context.put("a",1);
context.put("b",2);
context.put("c",3);
String express = "a+b*c";
Object r = runner.execute(express, context, null, true, true);
System.out.println(r);
}
}
说明
绑定java类或者对象的method,在业务中经常用到,可以重点关注,其余的亲自跑一下看看运行效果。
参考:
https://github.com/alibaba/QLExpress