目录
- enabled 是否启用当前规则
- dialect 指定方言
- salience 设置优先级
- date-effective、date-expires 规则有效期
- no-loop 是否循环匹配
- lock-on-active 增强版的no-loop
- activation-group 激活组
drl脚本基本结构
//指定包名
package rules.sign
//导入需要的类
import java.util.List
import com.chy.mall.entity.Sign
import org.slf4j.Logger
//定义全局变量,可以定义多个全局变量
global Logger logger
global List<Integer> dayCountRange
//此外还可以定义function函数 、query查询等内容
//定义规则,指定规则名称。可以定义多个规则,规则内部不能再嵌套规则。
rule sign_day_7
when
$sign:Sign(dayCount == 7)
then
$sign.setGoldCount(200);
System.out.println("签到规则匹配,成功匹配到规则sign_day_7:连续签到7天送200金币");
end
- 注释:单行 //,多行 /* */
- package要在drl脚本的最前面(除注释外),其他部分没有顺序要求
drl脚本常见组成
package 包
drools 中的包只是逻辑上的分包,相当于命名空间,不要求和 drl 文件的物理路径保持一致,但最好保持一致,不然应用启动时会报 warn。
示例:xxx.drl 在 classpath 的 rules/sign 文件夹下,则 package 建议使用 rules.sign。
import 导入
- 会自动导入jdk中的类,所以可以直接使用jdk中的类,无需手动导入;
- 自定义的类、在多个包下都有的同名类,需要手动 import 导入。
global 全局变量
//在drl脚本中声明全局变量,此处只能声明,不能直接赋值
global Logger logger
//通过 kieSession 给全局变量赋值
kieSession.setGlobal("logger", LoggerFactory.getLogger(this.getClass()));
- rule 中声明的是局部变量,只能在该 rule 中使用,全局变量可以在所在包的任何 rule 中使用。
- 全局变量只能使用引用类型,不能使用int、char之类的基本类型。
- 全局变量的生命周期对应一个 Fact 对象的匹配期间,一个 Fact 对象匹配完成后,全局变量会重置为 setGlobal() 设置的初始值,继续用于处理下一个 Fact 对象。
rule 规则
//指定规则名称。ruleName 可引可不引,包含空格时必须引;在同一包中,ruleName 不能重复
rule "ruleName"
attribute //设置规则属性
when
//LHS 规则条件
then
//RHS 规则计算
end
- 规则以 rule 开头、end 结尾,分为 attribute、LHS、RHS 3部分, attribute 可选,LHS、RHS 必需。
- attribute 用于设置当前规则的一些属性,比如是否可被重复执行、生效时间、过期时间等,可以设置多个规则属性,一行一个。
- Fact 对象与规则进行匹配时,先校验 attribute 设置的规则属性,满足才校验 when 中的规则条件,只有这2处都满足才激活规则、执行规则的 then 部分。
drools 处理 Fact 对象的流程:按drools加载规则的顺序,以规则为单位,逐个处理当前批次中的 Fact 对象。示例
一个drl脚本中依次定义了3个规则 rule1、2、3,规则优先级都使用默认值,则drools加载规则的先后顺序为 rule1、2、3;
一批次传递了3个Fact对象 Fact1、2、3;
处理流程:
①rule1处理当前批次中所有的Fact对象
②rule2处理当前批次中所有的Fact对象
③rule3处理当前批次中所有的Fact对象
如果一个Fact对象满足多个规则,会依次执行满足的规则。
常见属性(attribute)
属性可以放在 rule 中,只对该 rule 有效;也可以提到 rule 外作为全局属性配置,对所属包中的所有规则都有效。
enabled 是否启用当前规则
enabled true
默认true
dialect 指定方言
dialect "java"
支持 java、mvel,默认java,可以在then中写java代码
salience 设置优先级
salience 100
- 值是一个整数,可以为负数,默认为0。
- 数值越大优先级越高,越先进行规则匹配;优先级相同时,按规则的加载顺序(在drl脚本中的先后顺序)执行规则匹配。
date-effective、date-expires 规则有效期
//指定规则生效时间
date-effective "31-Dec-2021"
//指定规则失效时间
date-expires "31-Dec-2022"
- 执行规则匹配时会将系统时间与设置的规则生效|失效时间比较,时间范围满足时规则才生效;
- 均可选,缺省时默认规则一直有效。
默认格式 d-MMM-yyyy,比如 2021-12-31,如果操作系统是英文的,写成 “31-Jul-2017”;如果是操作系统是中文的,写成 “31-十二月-2021”。
可以使用自定义的格式,比如 “2021-12-31 10:57:00”,但需要在应用中设置 drools 使用的日期格式
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
注意:值必须与格式一一对应,比如格式是 “yyyy-MM-dd HH:mm:ss”,则值必须包含年月日时分秒,不能只有年月日,没有时分秒。
no-loop 是否循环匹配
rule order_discount_3
no-loop false
when
$order:Order(originalPrice >= 100 && originalPrice <= 1000)
then
//修改作为条件的Fact对象属性
$order.setOriginalPrice($order.getOriginalPrice() * 2);
//更新Fact对象
update($order)
end
默认值 false,允许循环匹配:如果 then 中修改了作为条件的 Fact 对象属性(注意要是作为条件的属性),且使用了 update() 更新 Fact 对象,则 update() 会触发当前规则执行完后,会将更新后的 Fact 对象与之前匹配过的规则进行再次匹配,如果这轮匹配中仍然触发 update() 了,会一直循环下去,直到不满足 update() 所在规则才退出循环、继续与后续规则进行匹配。
设置为 true 则一个 Fact 对象与一个规则只匹配一次,不管有没有对 Fact 对象作为条件的属性值进行了修改,不管是否使用了 update() 更新 Fact 对象,都不会触发 Fact 对象与之前规则的再次匹配。
通常设置为 true,避免出现循环匹配,以及循环匹配造成的死循环。
lock-on-active 增强版的no-loop
lock-on-active true
lock-on-active 的使用方式、效果和 no-loop 差不多,是 no-loop 的增强版。
no-loop、lock-on-active 都用于保证对同一个 Fact 对象,一个规则只匹配一次,在drl脚本中,一般都要将 lock-on-active 或 no-loop 设置为 true,避免规则的重复匹配。
activation-group 激活组
//值必须加引号
activation-group "xxx"
activation-group 将规则划分到指定激活组中,对于同一个激活组,只要当前 Fact 对象激活了其中的某个规则,就不会再与这个激活组中的其它规则匹配。
LHS 规则条件
多个条件
LHS 全称 Left Hand Side,可以包含 0~n 个条件,多个条件可以用 &&、|| 连接,&&也可以写成逗号,可以用小括号来区分优先级,不能使用单词形式的逻辑运算符 and、or、not。
为空时会自动添加一个条件:eval(true),此条件总是返回true,即所有 Fact 对象都满足条件。
when
$order:Order(originalPrice >= 100 && originalPrice <= 1000)
when
$order:Order(originalPrice >= 100 , originalPrice <= 1000)
条件中可以进行常见的数学运算,可以使用 >、<、==、!= 等比较运算符,可以使用 drools 内置的判断运算符,不能使用 xxx.equals()、StringUtils.isNotBlank() 等 java 代码。
drools内置的判断运算符
常用的如下
- contains | not contains 包含|不包含
- memberOf | not memberOf 属于|不属于
- matches | not matches 是否匹配正则表达式
//contains、memberOf 都可以使用字符串或集合,只要是对方的成员即可。字符串 => 只要字符串中包含即可,集合 => 等于其中某个元素即可
$user:User("男女" contains gender)
$user:User(["男","女"] contains gender)
$user:User(gender memberOf "男女")
$user:User(gender memberOf ["男","女"])
//正则表达式写成字符串格式,特殊字符要转义,比如\要使用\\
$user:User(tel matches "(13\\d|14[579]|15[^4\\D]|17[^49\\D]|18\\d)\\d{8}")
接收Fact对象
Fact对象:传递给drools脚本 | 规则 的对象,在规则中可以操作 Fact 对象,访问成员属性、调用成员方法。
语法
//限制条件可选
Fact对象的数据类型(【限制条件】)
一个规则的条件匹配包括两部分:Fact对象的数据类型要匹配、限制条件要满足。
示例
rule order_discount_1
when
//不带限制条件,只要Fact对象是Order类型就匹配
Order()
then
//...
end
rule order_discount_2
when
//带有限制条件,需要是Order类型且要满足限制条件才匹配
Order(originalPrice >= 100 && originalPrice <= 1000)
then
//...
end
绑定变量名
语法
变量名:Fact对象的数据类型(【限制条件】)
示例
rule order_discount_4
when
$order:Order(originalPrice >= 100 && getOriginalPrice() <= 1000)
then
System.out.println($order.orderStatus);
System.out.println($order.getOrderStatus());
end
绑定变量名是可选的,如果要在rule中的其它地方引用,就可以绑定一个变量名,通过变量名来引用。为了好区分,变量名通常会带前缀 $ (可选)。
LHS、RHS中都可以访问Fact对象的成员(实质是调用对应的getter方法)、调用Fact对象的成员方法,LHS中可以直接访问,RHS中需要通过变量名来访问。
RHS 规则计算
RHS 全称 Right Hand Side
- RHS中可以有 if 条件判断,但尽量把条件判断提取到LHS中;
- LRS中不能使用 java 代码,RHS中可以;
- RHS中可以使用 drools 的内置对象、内置函数。
内置对象 drools
//获取当前rule
drools.getRule()
//获取当前rule的name
drools.getRule().getName()
//获取包名
drools.getRule().getPackage()
drools.getRule().getPackageName()
内置方法 insert()
insert():往工作内存中插入一个Fact对象,效果和 ksession 的 insert() 差不多。
不管是 ksession 的 insert() 还是 drools 内置的 insert(),插入时都会校验是否已插入过,如果 Fact 对象对应的引用没变,则不会再插入,复用变量时要注意这点。
示例1
Order order = new Order(200.00, null);
kieSession.insert(order);
//再次insert
order.setOriginalPrice(600.00);
kieSession.insert(order);
rule order_discount_1
when
$order:Order(originalPrice < 300)
then
//再次insert
$order.setOriginalPrice(600.00);
insert($order);
end
这2个操作,多次 insert 的 Fact 对象都指向堆中的同一个对象,在第一次 insert 这个 Fact 对象后,后续都不会再插入这个 Fact 对象。
示例2
Order order = new Order(200.00, null);
kieSession.insert(order);
//再次insert
order = new Order(600.00, null);
kieSession.insert(order);
rule order_discount_1
when
$order:Order(originalPrice < 300)
then
//再次insert
$order = new Order(600.00, null);
insert($order);
end
这2个操作,多次 insert 的 Fact 对象是堆中不同的对象,2次 insert 都会插入。
内置方法 update()
update():更新工作内存中的指定对象。
规则引擎是以 rule 为单位加载 Fact 对象,每个 rule 加载的都是最初 insert 传递给 drools | rule 的 Fact 对象,即 LRS 是以最初的 Fact 对象来判断是否满足条件,RHS 是以 Fact 对象当前的值来计算。
示例
rule order_discount_1
when
$order:Order(originalPrice > 100)
then
//修改Fact对象的属性
$order.setOriginalPrice(1000.00);
// update($order)
end
rule order_discount_2
when
$order:Order(originalPrice > 200)
then
System.out.println($order.getOriginalPrice());
end
rule order_discount_3
when
$order:Order(originalPrice > 500)
then
System.out.println($order.getOriginalPrice());
end
//传递给drools一个 originalPrice=300.00 的Order对象
Order order = new Order(300.00, null);
kieSession.insert(order);
3个rule加载的Fact对象都是最初的 new Order(300.00, null),规则1、2满足条件,规则3不满足条件,规则2的 RHS 以Fact对象当前的值 originalPrice=1000.00 进行计算,输出 1000.00。
如果修改 Fact 对象后,使用drools内置方法 update() 更新 Fact 对象,则所有规则都会重新加载这个 Fact 对象,后续对此 Fact 对象的规则处理,LHS 也都是以此次更新的 Fact 对象进行条件判断。
eg. 规则1中的 update() 取消注释,则规则1、2、3都满足条件。