博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
QLExpress 规则引擎使用介绍
阅读量:5364 次
发布时间:2019-06-15

本文共 7084 字,大约阅读时间需要 23 分钟。

一个轻量级的类java语法规则引擎,作为一个嵌入式规则引擎在业务系统中使用。让业务规则定义简便而不失灵活。让业务人员就可以定义业务规则。支持标准的JAVA语法,还可以支持自定义操作符号、操作符号重载、函数定义、宏定义、数据延迟加载等

 

QLExpress的特性

1、编译执行:

编译生成基础指令后执行,性能能得到基本保障。执行过程:单词分解-->单词类型分析-->语法分析-->生成运行期指令集合-->执行生成的指令集合

runner.execute("10 * 10 + 1 + 2 * 3 + 5 * 2", null, true,null); 最后生成的指令:	1:LoadData 10	2:LoadData 10	3:OP : * OPNUMBER[2] 	4:LoadData 1	5:OP : + OPNUMBER[2] 	6:LoadData 2	7:LoadData 3	8:OP : * OPNUMBER[2] 	9:OP : + OPNUMBER[2] 	10:LoadData 5	11:LoadData 2	12:OP : * OPNUMBER[2] 	13:OP : + OPNUMBER[2]

2、支持标准的java语法、JAVA运算符号和关键字

import:引入一个包或者类,例如:import java.util.*;需要放在脚本的最前面new:创建一个对象,例如:new ArrayList();for:操作符号if:操作符号then:操作符号else:操作符号break: 终止循环continue: 绩效循环return: 返回
A、四则运算 : 10 * 10 + 1 + 2 * 3 + 5 * 2     B、boolean运算: 3 > 2 and 2 > 3     C、创建对象,对象方法调用,静态方法调用:new com.ql.util.express.test.BeanExample("张三").unionName("李四")     D、变量赋值:a = 3 + 5     E、支持 in,max,min:  (a in (1,2,4)) and (b in("abc","bcd","efg"))

3、自定义的关键字

include:在一个表达式中引入其它表达式。例如: include com.taobao.upp.b; 资源的转载可以自定义接口IExpressResourceLoader来实现,缺省是从文件中装载[]:匿名创建数组.int[][] abc = [[11,12,13],[21,22,23]];NewMap:创建HashMap. Map abc = NewMap(1:1,2:2);Map abc = NewMap("a":1,"b":2)NewList:串接ArrayList.List abc = NewList(1,2,3);exportDef: 将局部变量转换为全局变量,例如:exportDef long userIdalias:创建别名,例如: alias 用户ID user.userIdexportAlias: 创建别名,并转换为全局别名macro: 定义宏,例如: macro 降级  {level = level - 1}function: 定义函数,例如: function add(int a,int b){  return a+b; };in: 操作符号,例如: 3 in (3,4,5)mod:操作符号,例如:  7 mod 3 like:操作符号,例如: "abc" like 'ab%'

4、自定义的系统函数,后续还会不断的添加

max:取最大值max(3,4,5)   min:最最小值min(2,9,1)   round:四舍五入round(19.08,1)   print:输出信息不换行print("abc")   println:输出信息并换行 println("abc")

5、提供表达式上下文,属性的值不需要在初始的时候全部加入,而是在表达式计算的时候,需要什么信息才通过上下文接口获取。

避免因为不知道计算的需求,而在上下文中把可能需要的数据都加入。

runner.execute("三星卖家 and 消保用户",errorList,true,expressContext) "三星卖家"和"消保用户"的属性是在需要的时候通过接口去获取。

6、可以将计算结果直接存储到上下文中供后续业务使用。例如:

runner.execute("c = 1000 + 2000",errorList,true,expressContext);   则在expressContext中会增加一个属性c=3000,也可以在expressContext实现直接的数据库操作等。

7、支持高精度浮点运算,只需要在创建执行引擎的时候指定参数即可:new ExpressRunner(true,false);

8、可以将Class和Spring对象的方法映射为表达式计算中的别名,方便其他业务人员的立即和配置。例如

将 Math.abs() 映射为 "取绝对值"。      runner.addFunctionOfClassMethod("取绝对值", Math.class.getName(), "abs",new String[] { "double" }, null);       runner.execute("取绝对值(-5.0)",null,true,null);

9、可以为已经存在的boolean运算操作符号设置别名,增加错误信息同步输出,在计算结果为false的时候,同时返回错误信息,减少业务系统相关的处理代码

runner.addOperatorWithAlias("属于", "in", "用户$1不在允许的范围")。   用户自定义的函数同样也可以设置错误信息:例如:    runner.addFunctionOfClassMethod("isOk", BeanExample?.class.getName(),"isOk", new String[] { "String" }, "$1 不是VIP用户");   则在调用:     List errorList = new ArrayList?();      Object result =runner.execute("( (1+1) 属于 (4,3,5)) and isOk("玄难")",errorList,true,null);      执行结果 result = false.同时在errorList中还会返回2个错误原因:          1、"用户 2 不在允许的范围"         2、玄难 不是VIP用户

10、可以自定义函数,自定一个操作函数 group

class GroupOperator extends Operator {	public GroupOperator(String aName) {		this.name= aName;	}	public Object executeInner(Object[] list)throws Exception {		Object result = new Integer(0);		for (int i = 0; i < list.length; i++) {			result = OperatorOfNumber.Add.execute(result, list[i]);		}		return result;	}}

则执行:

runner.addFunction("累加", new GroupOperator("累加"));     runner.addFunction("group", new GroupOperator("group"));    //则执行:group(2,3,4)  = 9 ,累加(1,2,3)+累加(4,5,6)=21

11、可以自定操作符号。自定义的操作符号优先级设置为最高。例如自定一个操作函数 love:

class LoveOperator extends Operator {		public LoveOperator(String aName) {		this.name= aName;	}	public Object executeInner(Object[] list)			throws Exception {		String op1 = list[0].toString();		String op2 = list[1].toString();		String result = op2 +"{" + op1 + "}" + op2;				return result;	}}

然后增加到运算引擎:

runner.addOperator("love", new LoveOperator("love"));    //则执行:'a' love 'b' love 'c' love 'd' = "d{c{b{a}b}c}d"

12、可以重载已有的操作符号。例如替换“+”的执行逻辑。参见:com.ql.util.express.test.ReplaceOperatorTest

13、可以延迟运算需要的数据
14、一个脚本可以调用其它脚本定义的宏和函数.参见com.ql.util.express.test.DefineTest
15、可以类似VB的语法来使用操作符号和函数。print abc; 等价于 print(abc).参见 com.ql.util.express.test.OpCallTest
16、
17、对 in 操作支持后面的是一个数组或者List变量义

 

最典型的应用场景

在业务系统中存在一些逻辑判断,例如"商品A"只能是三星卖家,而且已经开通淘宝店铺的用户才能订购。
那么我们期望业务人员看到的配置信息就是:"三星卖家 而且 已经开店"
则通过下列步骤可能实现这个功能:

1、定义一个用户信息对象:

class UserInfo {	long id;	long tag;        String name;	public UserInfo(long aId,String aName, long aUserTag) {		this.id = aId;		this.tag = aUserTag;		this.name = aName;	}        public String getName(){    	   return name;        }	public long getUserId() {		return id;	}	public long getUserTag() {		return tag;	}}

2、定义两个基础的功能函数:

/**     * 判断一个用户TAG的第X位是否为1。这个的demo,其实现合理性不考虑     * @param user     * @param tagBitIndex     * @return     */    public boolean userTagJudge(UserInfo user,int tagBitIndex){    	boolean r =  (user.getUserTag() & ((long)Math.pow(2, tagBitIndex))) > 0;    	return r;    }		/**	 * 判断一个用户是否订购过某个商品	 * @param user	 * @param goodsId	 * @return	 */	public boolean hasOrderGoods(UserInfo user,long goodsId){		//随机模拟一个		if(user.getUserId() % 2 == 1){			return true;		}else{			return false;		}	}

3、初始化语句执行器

public void initial() throws Exception{		runner.addOperatorWithAlias("而且","and",null);		runner.addFunctionOfClassMethod("userTagJudge", Demo.class.getName(), "userTagJudge",                            new String[] {UserInfo.class.getName(),"int"}, "你不是三星卖家");		runner.addFunctionOfClassMethod("hasOrderGoods", Demo.class.getName(), "hasOrderGoods",                            new String[] {UserInfo.class.getName(),"long"}, "你没有开通淘宝店铺");		runner.addMacro("三星卖家", "userTagJudge(userInfo,3)");//3表示三星卖家的标志位		runner.addMacro("已经开店", "hasOrderGoods(userInfo,100)");//100表示旺铺商品的ID	}

4、定义一个逻辑判断函数:

/**	 * 判断逻辑执行函数	 * @param userInfo	 * @param expression	 * @return	 * @throws Exception	 */	public String hasPermission(UserInfo userInfo,String expression) throws Exception {			        	IExpressContext
expressContext = new DefaultContext
(); expressContext.put("userInfo",userInfo); List
errorInfo = new ArrayList
(); Boolean result = (Boolean)runner.execute(expression, expressContext, errorInfo, true, false); String resultStr =""; if(result.booleanValue() == true){ resultStr = "可以订购此商品"; }else{ for(int i=0;i
0){ resultStr = resultStr + "," ; } resultStr = resultStr + errorInfo.get(i); } resultStr = resultStr + ",所以不能订购此商品"; } return "亲爱的" + userInfo.getName() + " : " + resultStr; }

5、调用执行器执行判断逻辑:

public static void main(String args[]) throws Exception{		Demo demo = new Demo();		demo.initial();		System.out.println(demo.hasPermission(new UserInfo(100,"xuannan",7),  "三星卖家   而且   已经开店"));		System.out.println(demo.hasPermission(new UserInfo(101,"qianghui",8), "三星卖家   而且   已经开店"));		System.out.println(demo.hasPermission(new UserInfo(100,"张三",8), "三星卖家 and 已经开店"));		System.out.println(demo.hasPermission(new UserInfo(100,"李四",7), "三星卖家 and 已经开店"));

 

参考资料

 

转载于:https://www.cnblogs.com/duanxz/p/9307985.html

你可能感兴趣的文章
antiSMASH数据库:微生物次生代谢物合成基因组簇查询和预测
查看>>
UNICODE与ANSI的区别
查看>>
nginx 配置实例
查看>>
Flutter - 创建底部导航栏
查看>>
ASP.NET MVC 教程-MVC简介
查看>>
SQL Server索引 - 聚集索引、非聚集索引、非聚集唯一索引 <第八篇>
查看>>
转载:详解SAP TPM解决方案在快速消费品行业中的应用
查看>>
Android OpenGL ES 开发(N): OpenGL ES 2.0 机型兼容问题整理
查看>>
项目中用到的技术及工具汇总(持续更新)
查看>>
【算法】各种排序算法测试代码
查看>>
HDU 5776 Sum
查看>>
201521123044 《Java程序设计》第9周学习总结
查看>>
winfrom 图片等比例压缩
查看>>
人工智能实验报告一
查看>>
用LR12录制app,用LR11跑场景,无并发数限制,已试验过,可行!
查看>>
python 多线程就这么简单(转)
查看>>
oracle 简述
查看>>
ajax如何向后台传递数组,在后台该如何接收的问题(项目积累)
查看>>
Solr之java实现增删查操作
查看>>
httpClient连接工具类实测可用
查看>>