目录
一、什么是规则引擎
1.1 规则引擎概述
1.2 规则引擎执行过程
二、为什么要使用规则引擎
2.1 使用规则引擎的好处
2.1.1 易于维护和更新
2.1.2 增强应用程序的准确性和效率
2.1.3 加快应用程序的开发和部署
2.1.4 支持可视化和可管理性
2.2 规则引擎使用场景
三、常用规则引擎介绍
3.1 Drools
3.1.1 Drools简介
3.1.2 Drools特点
3.1.3 Drools执行流程
3.2 EasyRules
3.2.1 EasyRules功能特性
3.3 uRules
3.3.1 URules特点
3.3.2 URules优势
3.3.2.1 功能强大
3.3.2.2 使用简单
3.3.2.3 性能优异
3.3.2.4 完善的版本控制机制
3.4 jvs-rules
3.4.1 jvs-rules 特点
3.4.2 jvs-rules 核心能力
3.4.2 jvs-rules技术优势
3.5 QLExpress
3.5.1 QLExpress概述
3.5.2 QLExpress特点
四、Drools使用
4.1 案例操作步骤
4.1.1 maven引入核心依赖
4.1.2 增加规则配置文件
4.1.3 定义Drools配置类
4.1.4 业务实现类
4.1.5 业务接口
4.1.6 效果测试
4.2 Drools语法规则说明
4.2.1 基本语法规则
4.2.2 Drools规则文件完整内容
4.2.3 Drools注释说明
4.2.4 Pattern模式匹配
4.2.5 dialect 属性
4.2.6 比较操作符
五、QLExpress使用
5.1 QLExpress 运行原理
5.2 QLExpress 运算符
5.2.1 引入依赖
5.2.2 入门案例
5.2.3 QLExpress常用运算符
5.3 QLExpress API使用
5.3.1 自定义表达式
5.3.2 集合操作
5.3.3 对象操作
5.3.4 函数操作
5.3.5 预定义变量
5.3.6 绑定java方法
5.3.7 自定义操作符
六、写在文末
规则引擎,全称业务规则管理系统,规则引擎主要思想是将应用程序中的业务决策部分剥离出来,并使用预定义的语言模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理和使用。
规则引擎是一种嵌入在应用程序中的组件,具体使用时接受外部数据输入,解释业务规则,并根据业务规则做出决策,具体来说:
注意:规则引擎并不是是一个具体的实现框架,而是指的是一类系统,即业务规则管理系统,市面上有很多规则引擎的产品,简单来说,规则引擎就是一个输入输出的平台。
规则引擎的核心执行流程如下:
规则引擎将复杂的业务逻辑从业务代码中剥离出来,可以显著降低业务逻辑实现难度,同时,剥离的业务规则使用规则引擎实现,这样可以使多变的业务规则变得可维护,配合规则引擎提供的良好的业务规则设计器,不用编码就可以快速实现复杂的业务规则。同样,即使是完全不懂编程的业务人员,也可以轻松上手使用规则引擎来定义复杂的业务规则。具体来说:
规则引擎可使业务规则与系统代码分离,从而降低维护和更新的难度。通过更新规则库或配置文件中的规则,可以快速地修改业务逻辑和规则变化。像下面这样的代码随处可见,如果能使用规则引擎统一配置管理,就可以避免很多冗余而臃肿的业务逻辑。
规则引擎能够处理复杂和动态的规则,可以有效地提高应用程序的准确性和效率。通过使用规则引擎,可以帮助用户快速解决复杂的业务问题和数据分析。
类似于种类繁多计算场景,如果使用编程去处理,很容易遗漏或出错,但规则引擎中封装的规则、算法则经历了长期的实践考研,具备充分的可靠性和准确性
规则引擎可以提高开发效率和开发质量,降低测试和维护成本,从而提高企业效率和效益。
规则引擎可以通过图形用户界面和数据可替代性,可以更好地管理规则库和规则的版本控制。
像Drools,uRules等规则引擎工具都提供了可视化的规则配置管理控制台
规则引擎常用的应用场景如下:
发展到今天,市面上出现了很多成熟的规则引擎解决方案,下面选择几种常用的规则引擎进行说明。
drools是一款由JBoss组织提供的基于java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。
drools官网:https://www.drools.org/
drools中文网:Drools中文网 | 基于java的功能强大的开源规则引擎
drools源码下载地址:https://github.com/kiegroup/drools
Drools具有如下特点:
Drools 使用 ReteOO算法执行规则。支持使用自然语言表达业务逻辑,也可以使用 Java/Groovy/Python + XML 语法编写规则。
早期的版本一般由开发人员通过开发工具插件来定义规则,目前已有Drools Workbench通过web服务提供给业务人员维护规则。
如下图是Drools执行规则时的流程图
Drools执行时主要的业务流程和内部的组件如上图所示,Drools规则引擎基于以下抽象组件实现:
当用户或系统在Drools中添加或更新规则相关的信息时,该信息会以一个或多个事实的形式插入Drools规则引擎的工作内存中。Drools规则引擎匹配事实和存储在生产内存中规则,筛选符合执行条件的规则。对于满足条件的规则,规则引擎会在议程中激活和注册对应的规则,在议程中Drools会进行优先级的排序和冲突的解决,准备规则的执行。
asyRules是一款基于Java的开源的轻量级的规则引擎框架,可以帮助开发人员快速开发并管理规则,实现应用程序的自动化决策。EasyRules框架非常易于使用,且可以与任何Java应用程序无缝集成,可以实现复杂的规则表达式匹配。EasyRules是一个基于规则的引擎,它基于规则引擎的常见原则和概念。以下是一些EasyRules框架中的重要概念:
Easy Rules是一个简单但功能强大的Java规则引擎,提供以下特性:
URule Pro是一款国产化的自主研发的一款纯Java规则引擎,可以运行在Windows、Linux、Unix等各种类型的操作系统之上; URule Pro的规则设计器采用业内首创的纯浏览器编辑模式,无须安装任何工具,打开浏览器即可完成复杂规则的设计与测试。
uruls学习文档:1.简介 · URule规则引擎使用指南
URules具有如下特点:
uRules技术架构图
在URule Pro当中,提供规则集、决策表、交叉决策表(决策矩阵)、决策树、评分卡、复杂评分卡、规则流等八种类型的业务规则设计工具,从各个角度满足复杂业务规则设计的需要。
在URule Pro当中,提供规则集、决策表、交叉决策表(决策矩阵)、决策树、评分卡、复杂评分卡、规则流等八种类型的业务规则设计工具,从各个角度满足复杂业务规则设计的需要。
URule Pro中提供的所有的规则设计器及打包测试工具,全部基于浏览器实现,所有的规则设计器皆为可视化、图形化设计器,通过鼠标点击即可实现复杂的业务规则定义,URule Pro中规则的多条件组合也是以图形方式展现,这样即使没有任何编程经验的普通业务人员,也可以轻松上手,完成复杂业务规则的定义。
因为所有的业务规则设计器都是基于网页的,且规则的定义都是通过鼠标点击的方式完成,所以对于一个普通的使用者来说,配合教学视频两到三天即可完全掌握URule Pro中各种设计器的使用,结合业务需要定义出想要的业务规则。
URule Pro后台采用纯Java实现,运行时借鉴Rete了算法的优势,再结合中式规则引擎的特点,独创了一套自己的规则模式匹配算法,这套算法可以从根本上保证规则运行的效率,实现大量复杂业务规则计算时的毫秒级响应。
在URule Pro当中,无论是单个规则文件、或是用户调用的规则包,都提供了完善的版本控制机制。对于规则文件来说只要有需要,可以回退到任何一个历史版本; 对于给用户调用的规则包,可以在不同的历史版本之间灵活切换。
JVS-RULES规则引擎是一款可视化的业务规则设计器,它的核心功能在于可集成多种数据源,包括多种类型数据库和API,将数据进行界面可视化的加工处理后,形成策略节点所需要的各种变量,然后通过规则的可视化编排,形成各种决策场景,让业务系统可以快速简单的调用,在线实时获得业务决策结果。
gitee开源地址:https://gitee.com/software-minister/jvs-rules
在线体验地址:http://rules.bctools.cn
良好的集成性
JVS-rules可以在界面上快速集成数据,通过可视化的配置无需硬编码,可实现数据库、API的接入。
使用灵活
规则引擎使用可视化编排工具,允许用户以拖拽方式构建规则,降低使用难度,提高工作效率。
上手简单
提供基于界面的操作方式,并提供大量的操作文档与学习视频,快速了解并掌握产品的使用方法。
数据集成加工
将不同来源的数据进行整合,通过变量界面化对数据进行加工,挖掘数据业务含义
构建决策建模
利用数据变量,结合各种策略模式,构建预测各种判断模型、业务决策
在线决策支持
提供快速、标准的调用模式,企业的业务系统可简单、高效的接入系统,便捷高效的完成在线决策
QLExpress由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。
git源码地址:https://github.com/tanqiwei/QLExpressionStudy
QLExpress具有如下特点:
相信很多有过网购经验的同学对购物时商品打折不陌生,比如某电商网站推出了根据不同等级的会员享受不同的折扣,钻石级7.5折,金卡8.5折,银卡9折,同时为了吸引客户,再叠加其他的优惠策略,购买当前商品的次数达到2次再减10元,达到5次减30元,超过5次减50元....
类似这样的场景还有很多,接下来我们使用Drools来实现一个类似的功能。按照下面的操作步骤执行
org.springframework.boot
spring-boot-starter-parent
2.3.4.RELEASE
7.59.0.Final
org.drools
drools-compiler
${drool.version}
org.drools
drools-core
${drool.version}
org.drools
drools-decisiontables
${drool.version}
org.drools
drools-mvel
${drool.version}
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
在resources目录下创建一个文件夹,用于保存Drools的规则配置文件,规则文件名称为,customer-discount.drl,在里面定义对应的规则,如果需要更多的规则继续在后面追加
import com.congge.model.OrderRequest;
import com.congge.model.CustomerType;
global com.congge.model.OrderDiscount orderDiscount;
dialect "mvel"
// 规则1: 根据年龄判断
rule "Age based discount"
when
// 当客户年龄在20岁以下或者50岁以上
OrderRequest(age < 20 || age > 50)
then
// 则添加10%的折扣
System.out.println("==========Adding 10% discount for Kids/ senior customer=============");
orderDiscount.setDiscount(orderDiscount.getDiscount() + 10);
end
// 规则2: 根据客户类型的规则
rule "Customer type based discount - Loyal customer"
when
// 当客户类型是LOYAL
OrderRequest(customerType.getValue == "LOYAL")
then
// 则增加5%的折扣
System.out.println("==========Adding 5% discount for LOYAL customer=============");
orderDiscount.setDiscount(orderDiscount.getDiscount() + 15);
end
rule "Customer type based discount - others"
when
OrderRequest(customerType.getValue != "LOYAL")
then
System.out.println("==========Adding 3% discount for NEW or DISSATISFIED customer=============");
orderDiscount.setDiscount(orderDiscount.getDiscount() + 3);
end
rule "Amount based discount"
when
OrderRequest(amount > 1000L)
then
System.out.println("==========Adding 5% discount for amount more than 1000$=============");
orderDiscount.setDiscount(orderDiscount.getDiscount() + 5);
end
在这个规则配置文件中,定义了3种规则,
该类相当于在spring容器启动加载后全局注册了一个KieContainer的bean,bean注册到容器中时会加载上述的规则配置文件,从而将规则实例化到bean容器,而后我们就可以使用KieContainer中相关的API进行规则的使用。
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DroolsConfig {
private static final String RULES_CUSTOMER_RULES_DRL = "ruls/customer-discount.drl";
private static final KieServices kieServices = KieServices.Factory.get();
@Bean
public KieContainer kieContainer() {
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_CUSTOMER_RULES_DRL));
KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);
kb.buildAll();
KieModule kieModule = kb.getKieModule();
KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());
return kieContainer;
}
}
在实际业务中,如何将业务中的数据交由Drools去执行呢,一个正常的思路就是,通过接口将外部传入的参数进行解析,然后传给上面的Container,由Container去执行规则即可,请看下面的业务方法
import com.congge.model.OrderDiscount;
import com.congge.model.OrderRequest;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderDiscountService {
@Autowired
private KieContainer kieContainer;
public OrderDiscount getDiscount(OrderRequest orderRequest) {
OrderDiscount orderDiscount = new OrderDiscount();
// 开启会话
KieSession kieSession = kieContainer.newKieSession();
// 设置折扣对象
kieSession.setGlobal("orderDiscount", orderDiscount);
kieSession.insert(orderRequest);
// 触发规则,交给规则引擎进行计算
kieSession.fireAllRules();
//通过规则过滤器实现只执行指定规则
//kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("Age based discount"));
kieSession.dispose();
return orderDiscount;
}
}
上述业务类用到的其他类
OrderRequest,请求参数对象,定义了接口传参的相关属性,该类也将传递到自定义的引擎规则配置文件中进行计算
@Data
public class OrderRequest {
/**
* 客户号
*/
private String customerNumber;
/**
* 年龄
*/
private Integer age;
/**
* 订单金额
*/
private Integer amount;
/**
* 客户类型
*/
private CustomerType customerType;
}
定义一个客户类型CustomerType 的枚举,规则引擎会根据该值计算客户订单折扣百分比
public enum CustomerType {
LOYAL, NEW, DISSATISFIED;
public String getValue() {
return this.toString();
}
}
订单折扣类 OrderDiscount
,用来表示计算得到的最终的折扣
@Data
public class OrderDiscount {
private Integer discount = 0;
}
自定义一个接口,用于测试计算规则的结果
@RestController
public class OrderDiscountController {
@Autowired
private OrderDiscountService orderDiscountService;
@PostMapping("/get/discount")
public OrderDiscount getDiscount(@RequestBody OrderRequest orderRequest) {
OrderDiscount discount = orderDiscountService.getDiscount(orderRequest);
return discount;
}
}
使用postman测试一下,可以控制不同的输入参数然后观察输出结果
上面通过一个简单的案例快速演示了一下Drools的使用,接下来深入了解下Drools的使用规则
使用 Drools 时非常重要的一个事情就是编写规则文件,规则文件的后缀为通常为.drl,drl是Drools Rule Language的缩写,然后在规则文件中编写具体的规则内容即可。一个完整的drl中定义的规则体结构如下:
rule "ruleName"
attributes
when
LHS
then
RHS
end
补充说明:
不难理解,其实Drools规则引擎的语法和我们编写js或Java代码非常相似,上手编写规则文件只要遵循一定的规范,非Java开发人员也可以快速上手。
下面总结了Drools编写规则文件中常用的属性和内容
关键字 | 描述 |
package | 包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用 |
import | 用于导入类或者静态方法 |
global | 全局变量 |
function | 自定义函数 |
query | 查询 |
rule end | 规则体 |
Drools支持的规则文件,除了drl形式,还有Excel文件类型的。
在drl形式的规则文件中使用注释和Java类中使用注释一致,分为单行注释和多行注释。
单行注释用//进行标记,多行注释以/开始,以/结束。如下示例:
//规则rule1的注释,这是一个单行注释
rule "rule1"
when
then
System.out.println("rule1触发");
end
/*
规则rule2的注释,
这是一个多行注释
*/
rule "rule2"
when
then
System.out.println("rule2触发");
end
前面的学习我们已经知道Drools 中的匹配器可以将 Rule Base 中的所有规则与Working Memory中的Fact对象进行模式匹配,那么我们就需要在规则体的LHS部分定义规则并进行模式匹配。LHS部分由一个或者多个条件组成,条件又称为pattern。
pattern语法结构
绑定变量名: Object(Field约束)
其中 绑定变量名可以省略,通常绑定变量名的命名一般建议以 $ 开始。如果定义了绑定变量名,就可以在规则体的 RHS 部分使用此绑定变量名来操作相应的Fact对象。Field约束部分是需要返回true或者false的0个或多个表达式。如下代码示例:
规则1:
//总价在100到200元的优惠20元
rule "rule_discount_1"
when
//Order为类型约束,originalPrice为属性约束
$order:Order(originalPrice < 200 && originalPrice >= 100)
then
$order.setRealPrice($order.getOriginalPrice() - 20);
System.out.println("成功匹配到规则rule_discount_1:商品总价在100到200元的优惠20元");
end
规则2:
//规则二:总价在100到200元的优惠20元
rule "rule_discount_2"
when
$order:Order($op:originalPrice < 200 && originalPrice >= 100)
then
System.out.println("$op=" + $op);
$order.setRealPrice($order.getOriginalPrice() - 20);
System.out.println("rule_discount_2:商品总价在100到200元的优惠20元");
end
LHS部分还可以定义多个pattern,多个pattern之间可以使用and或者or进行连接,也可以不写,默认连接为and,如下示例:
//规则3:总价在100到200元的优惠20元
rule "rule_discount_3"
when
$order:Order($op:originalPrice < 200 && originalPrice >= 100) and
$customer:Customer(age > 20 && gender=='male')
then
System.out.println("$op=" + $op);
$order.setRealPrice($order.getOriginalPrice() - 20);
System.out.println("rule_discount_3:商品总价在100到200元的优惠20元");
end
drools 支持两种 dialect:java 和mvel,可以理解为引擎识别的一种方言规则
package 和 rule 都可以指定 dialect 属性,mvel 是一种表达式语言, github主页为 git地址 , 文档主页为 :mvl文档,dools 中的 mvel dialect 可以认为是 java dialect 的超集, 也就是说 mvel dialect 模式下, 也支持 java dialect 的写法,mvel 和 java 的主要区别:
1)对于POJO 对象, java dialect 必须使用 getter 和 setter 方法;
2)对于POJO 对象, mvel dialect 可以直接使用属性名称进行读写, 甚至是 private 属性也可以;
如下,我们定义dialect为Java
rule "java_rule"
enabled true
dialect "java"
when
$order:Order()
then
System.out.println("java_rule fired");
$order.setRealPrice($order.getOriginalPrice() * 0.8) ;
end
如果使用mvl,如下示例
rule "mvel_rule"
enabled false
dialect "mvel"
when
$order:Order()
then
System.out.println("mvel_rule fired");
$order.realPrice=$order.originalPrice*0.7 ;
end
在编写规则文件时,离不开各种操作符的使用,Drools提供的比较操作符有:>、<、>=、<=、==、!=、contains 、not contains、memberOf 、not memberOf、matches 、not matches
操作符 | 说明 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
contains | 检查一个Fact对象的某个属性值是否包含一个指定的对象值 |
not contains | 检查一个Fact对象的某个属性值是否不包含一个指定的对象值 |
memberOf | 判断一个Fact对象的某个属性是否在一个或多个集合中 |
not memberOf | 判断一个Fact对象的某个属性是否不在一个或多个集合中 |
前面几个比较操作符和我们日常编程中基本类似,关于后面几个操作符在使用上做一下补充
contains | not contains
Object(Field[Collection/Array] contains value)
Object(Field[Collection/Array] not contains value)
memberOf | not memberOf
Object(field memberOf value[Collection/Array])
Object(field not memberOf value[Collection/Array])
matches | not matches
Object(field matches “正则表达式”)
Object(field not matches “正则表达式”)
从使用经验来说,Drools相对较重,此时你可以考虑使用QLExpress,可以说QLExpress的在使用上与Drools差别不大,同时语法灵活,规则编写容易上手,下面来详细介绍下QLExpress的使用。
官方地址:官方文档
QLExpress整体运行架构如下
针对上图中的执行步骤,做如下补充说明:
其中前4个过程涉及语法的匹配运算等非常耗时,可以设置execute方法的 isCache ,是否使用Cache中的指令集参数,它可以缓存前四个过程。
即把express语句在本地缓存中换成一段指令,第二次重复执行的时候直接执行指令,极大的提高了性能。或者ExpressRunner设置成singleton(结合spring是非常容易做到的)。
com.alibaba
QLExpress
3.2.0
通过下面这个入门案例体验下QLExpress的使用
public static void main(String[] args) throws Exception {
ExpressRunner runner = new ExpressRunner();
/**
* 表达式计算的数据注入接口
*/
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);
}
使用QLExpress进行编码时,主要包括3步:
QLExpress支持普通的Java语法执行,利用这些运算符,可以很好的满足复杂的业务场景下规则的计算,属性java或js语法的对此应该不陌生。下面总结了常用的运算符:
运算符 | 说明 | 示例 |
---|---|---|
+,-,*,/,<,>,<=,>=,==,!=,%,++,– | mod 等同于% |
a * b |
in,like,&&,! | in,like类似于sql语法 | a in [1,2,3] |
for,break、continue,if then else | 1、不支持try{}catch{} 2、不支持java8的lambda表达式 3、不支持for循环集合操作 4、弱类型语言,请不要定义类型声明,更不要用Templete 5、array的声明不一样 6、min,max,round,print,println,like,in 都是系统默认函数的关键字,请不要作为变量名 |
int n=10; int sum=0; for(int i=0;i |
其他运算符补充,比如逻辑运算符,如下:
运算符 | 说明 | 示例 |
---|---|---|
位运算 | ^,~,&,|,<<,>> | 1<<2 |
四则运算 | +,-,*,/,%,++,-- | 3%2 |
Boolean运算 | !,<,>,<=,>=,==,!=,&&,|| | 2==3 |
其他运算 | =,?: | 2>3?1:0 |
部分运算符在编码中的使用说明:
运算符 | 描述 | 运算符 | 描述 |
---|---|---|---|
mod | 与 % 等同 |
for | 循环语句控制符 |
return | 进行值返回 | if | 条件语句控制符 |
in | 类似sql语句的in | then | 与if同用 |
exportAlias | 创建别名,并转换为全局别名 | else | 条件语句控制符 |
alias | 创建别名 | break | 退出循环操作符 |
macro | 定义宏 | continue | 继续循环操作符 |
exportDef | 将局部变量转换为全局变量 | function | 进行函数定义 |
import | 引入包或类,需在脚本最前头 | class | 定义类 |
NewMap | 创建Map | NewList | 创建集合 |
like | 类似sql语句的like | new | 创建一个对象 |
在使用中,就是根据实际的业务场景进行各种运算符、函数等组合使用,掌握了这些运算符的使用基本上就搞清了QLExpress 的使用
自定义表达式在一些复杂的场景中可以非常灵活的进行规则编排,只要遵循基本的编码规范和QLExpress中的运算符规范即可
/**
* 语法基本说明:
* 不支持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);
}
这里主要介绍比较常用的两种集合,map和list的操作
map集合操作方式1:
这种方式的操作需要参考官方文档遵循相关的编码格式规范
/**
* 集合操作
* @param runner
* @throws Exception
*/
private static void collectionStatement(ExpressRunner runner) throws Exception {
DefaultContext context = new DefaultContext();
String express = "abc = NewMap(1:1,2:2); return abc.get(1) + abc.get(2);";
Object rMap = runner.execute(express, context, null, false, false);
System.out.println("map集合操作结果:" + rMap);
express = "abc = NewList(1,2,3); return abc.get(1)+abc.get(2)";
Object rList = runner.execute(express, context, null, false, false);
System.out.println("list集合操作结果:" + rList);
express = "abc = [1,2,3]; return abc[0]+abc[2];";
Object rArr = runner.execute(express, context, null, false, false);
System.out.println("arr集合操作结果 :" + rArr);
}
map集合操作方式2:
/**
* 集合操作
* @param runner
* @throws Exception
*/
private static void collectionStatement2(ExpressRunner runner) throws Exception {
DefaultContext 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
list集合操作方式:
public static void collectionStatement3(ExpressRunner runner) throws Exception {
DefaultContext defaultContext = new DefaultContext<>();
List list = Arrays.asList("ng", "si", "umid", "ut", "mac", "imsi", "imei");
defaultContext.put("list", list);
String listStatement =
"for(i=0;i
QLExpress引擎执行时也支持对对象参数的解析,参考下面的示例
实际业务开发中这是一种比较常用的方式,因为很多界面操作的交互都需要通过接口传递对象参数
/**
* 对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 com.congge.expression.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);
}
也可以自定义函数,然后再在execute方法中调用,函数的方式可以说是QLExpress的一大特色,因为不管是后端开发人员还是前端人员,或者对代码稍有了解的同学都可以编写函数使用
/**
* 自定义在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);
}
预定义变量的方式,即可以直接在执行规则之前,将规则初始化到执行器中,对于那些比较简单的执行规则,使用预定义变量的方式还是值得推荐的
/**
* 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引擎支持将java中的一些内置方法绑定到执行器上
/**
* 将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);
}
也可以自定义一些操作符,与QLExpress执行器中支持的表达式进行映射之后配合使用,一般使用场景很少
/**
* 操作符处理,一般不太常用,但可以自定义一些操作符
* @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);
}
更多的场景可以结合官方文档对照学习研究
本文通过较大的篇幅详细总结了常用的规则引擎的使用,最后介绍了Drools与QLExpress的详细使用,通过对比,可以在实际开发中进行技术选型时提供一个参考,本篇到此结束,感谢观看。