规则引擎
概述: 将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理。
产品:drools、VisualRules、iLog等。
运行: 接收数据输入,解释业务规则,并根据业务规则做出业务决策。输入输出平台
Drools
drools API 开发步骤
1. 获取KieServices
2. 获取KieContainer
3. KieSession
4. Insert fact
5. 触发规则
6. 关闭KieSession
入门实例 Maven 项目
-
创建Maven项目
-
引入配置 drool-complier dependence,新建配置文件 resource/META-INF/kmodule.xml (位置及名称必须保持一致)
<dependencies> <dependency> <groupId>org.drools</groupId> <artifactId>drools-compiler</artifactId> <version>7.10.0.Final</version> </dependency> </dependencies>
<kmodule xmlns="http://www.drools.org/xsd/kmodule"> <kbase name="myKbasel" packages="rules" defautlt="true"> <ksession name="ksession-rule" default="true" /> </kbase> </kmodule>
-
建立实体类
package com.item.drools.entity public Class Order{ private float originalPrice; private float realPrice; }
-
创建规则文件 resource/rules/*.drl,编写业务规则 (业务规则存放路径在kmodule.xml中进行配置)
package bookDiscount import com.item.drools.entity.Order rule "dook-discount-1" when $order: Order(originalPrice > 100) // 模式匹配 then $order.setRealPrice($order.getOriginalPrice()) System.out.Println("成功匹配规则一") end
-
编写单元测试
@Test public void test1(){ KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.newKieClasspathContainer(); KieSession session = kieContainer.newKieSession(); // Fact 对象 Order order = new Order(); order.setOriginalPrice(50d); System.out.println(order.getRealPrice()); // 输出null //将Order对象插入工作内存中 // 通过session对象与规则引擎进行交互 session.insert(order); // 激活规则 由Drools框架自动进行规则匹配 如果规则匹配成功 则执行当前规则 session.fireAllRules(); // 关闭会话 session.dispose(); System.out.println(order.getRealPrice()); // 输出匹配业务规则后的决策值 }
小结
规则引擎构成
drools 规则引擎由以下三部分构成:
- Working Memory 工作内存
- Rule Base 规则库
- Inference Engine 推理引擎
其中,推理引擎包括:
- Pattern Marcher 匹配器
- Agenda 议程
- Execution Engine 执行引擎
相关概念
Working Memory 工作内存,drools规则引擎会从Working Memory中获取数据并和规则文件中定义的规则进行模式匹配,因此开发的应用程序只需将数据插入到Working Memory
session.insert(order);
Fact 事实,是指在drools规则应用中,将一个普通的JavaBean插入到Working Memory后的对象就是Fact对象,例如入门案例中的Order对象就属于Fact对象,Fact对象是开发的应用程序与规则引擎进行数据交互的桥梁
Rule Base 规则库,在规则文件中定义的规则都会被加载到规则库中
Pattern Matcher 匹配器,将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,匹配成功的规则将被激活并放入Agenda中
Agenda y议程,用于存放通过匹配器进行模式匹配后的被激活的规则
Execution Engine 执行引擎,执行Agenda中被激活的规则
规则引擎执行过程
- 将初始数据(fact) 输入至工作内存 (Working Memory)
- 使用Pattern Matcher 将规则库中的规则 (rule) 和数据 (fact) 比较
- 如果执行规则存在冲突 (conflict) ,即同时激活了多个规则,将冲突的规则放入冲突集合
- 解决冲突,将激活的规则按顺序放入Agenda
- 执行Agenda中的规则,重复步骤2~5,直到执行完毕Agenda中所有规则
Drools基本语法
规则文件构成
一个完整的规则文件内容构成如下:
关键字 | 描述 |
---|---|
package | 包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用 |
import | 用于导入类或静态方法 |
global | 全局变量 |
function | 自定义函数 |
query | 查询 |
rule end | 规则体 |
Drools支持的规则文件,除了drl形式,还可以是Excel文件类型
规则体语法结构
rule "ruleName"
attributes
when
LHS
then
RHS
end
rule 关键字,表示规则的开始,参数为规则的唯一名称
attributes 规则属性,是rule与when之间的参数,为可选项
when 关键字,后面跟随规则的条件部分
LHS(Left Hand Side) 规则的条件部分的通用名称,由零个或多个条件元素组成;如果LHS为空,则它将被视为始终为true的条件元素
then 关键字,后面跟随规则的结论部分
RHS(Right Hand Side) 规则的后果或者行动部分的通用名称
end 关键字,表示一个规则的结束
注释
drl文件中的注释方式与java语法相同,分为单行注释(//) 或者多行注释 (/* */)
// 这是一个单行注释
rule "book-discount-1"
when
then
System.out.println("始终匹配")
end
/*
这是一个
多行
注释
*/
rule "book-discount-2"
when
then
System.out.println("始终匹配")
end
Pattern 模式匹配
Drools中的匹配器可以将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,那么我们就需要在规则体的LHS部分定义规则并进行模式匹配。LHS部分由一个或多个条件组成,条件又称为pattern。
pattern的语法结构为:
绑定变量名:Object(Field约束)
其中,绑定的变量名可以省略,通常绑定的变量名的命名一般建议以$开始。若定义了绑定变量名,就可以在规则体的RHS部分使用此绑定变量名来操作相应的Fact对象。Field约束部分是需要返回true或者false的零个或多个表达式
rule "book_discount_2"
when
//Order为类型约束,originalPrice为属性约束
$order:Order(originalPrice < 200 && originalPrice >= 100)
then
$order.setRealPrice($order.getOriginalPrice() - 20);
System.out.println("成功匹配规则二,所购图书总价在100到200的优惠20元");
end
通过上面的例子,匹配的条件为:
- 工作内存中必须存在Order这种类型的Fact对象 —— 类型约束
- Fact对象的originalPrice属性值必须小于200 ——属性约束
- Fact对象的originalPrice属性值必须大于等于100 ——属性约束
以上条件必须同时满足当前规则才有可能被激活
绑定变量既可以用在对象上,也可以用在对象的属性上。例如,上面的例子可以改为:
rule "book_discount_2"
when
//Order为类型约束,originalPrice为属性约束
// $op 绑定在属性 originalPrice 上
$order:Order($op:originalPrice < 200 && originalPrice >= 100)
then
System.out.println("$op=" + $op);
$order.setRealPrice($order.getOriginalPrice() - 20);
System.out.println("成功匹配规则二,所购图书总价在100到200的优惠20元");
end
LHS部分还可以定义多个pattern,多个pattern之间可以使用and 或者 or 进行连接,也可以不写,默认连接为 and。
rule "book_discount_2"
when
//Order为类型约束,originalPrice为属性约束
// $op 绑定在属性 originalPrice 上
//pattern 1
$order:Order($op:originalPrice < 200 && originalPrice >= 100) and
// pattern 2
$customer:Customer(age > 20 && gender == 'male')
then
System.out.println("$op=" + $op);
$order.setRealPrice($order.getOriginalPrice() - 20);
System.out.println("成功匹配规则二,所购图书总价在100到200的优惠20元");
end
比较操作符
Drools提供的比较操作符如下表:
符号 | 说明 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
contains | 检查一个Fact对象的某个属性值是否包含一个指定的对象值 |
not contains | 检查一个Fact对象的某个属性值是否不包含一个指定的对象值 |
memberOf | 检查一个Fact对象的某个属性值是否在一个或多个集合中 |
not memberOf | 检查一个Fact对象的某个属性值是否不在一个或多个集合中 |
matches | 检查一个Fact对象的某个属性值是否与提供的标准的java正则表达式进行匹配 |
not matches | 检查一个Fact对象的某个属性值是否不与提供的标准的java正则表达式进行匹配 |
-
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所提供的方式是通过规则过滤器来实现执行指定规则,对于规则文件不用做任何修改,只需要修改java代码即可。
KieServices kieServices = KieServices.Factory.get();
KieContainer kieContainer = kieServic.getKieClasspathContainer();
KieSession session = kieContainer.newKieSession();
//Fact对象
Order order = new Order();
session.insert(order);
// 规则过滤器 AgendaFilter
//执行指定名称为"rule_name"的规则
session.fireAllRules(new RuleNameEqualsAgendaFilter("rule_name"));
//执行规则名称以"rule_"开头的规则
session.fireAllRules(new RuleNameStartsWithAgendaFilter("rule_"));
session.dispose();
关键字
Drools关键字分为:硬关键字 (Hard Keywords) 和 软关键字 (Soft Keywords)。
硬关键字是我们在规则文件中定义报名或者规则名时明确不能使用的,否则程序会报错;软关键字虽然可以使用,但不建议使用。
类型 | 集合 |
---|---|
硬关键字 | true false null |
软关键字 | lock-on-active date-effective date-expires no-loop auto-focus activation-group agenda-group ruleflow-group entry-point duration package import dialect salience enabled attributes rule extend when then template query declare function global eval not in or and exists forall accumulate collect from action reverse result end over init |
Drools内置方法
规则文件的***RHS***部分的主要作用是通过插入、删除或者修改工作内存中的Fact数据,来达到控制规则引擎执行的目的。
Drools提供了一些方法可以用来操作工作内存中的数据,操作完成后规则引擎会重新进行相关规则的匹配,原来没有匹配成功的规则在我们修改数据完成后有可能就会匹配成功了。
- Update方法
KieServices kieServices = KieServices.Factory.get();
KieContainer kieContainer = kieService.newKieClasspathContainer();
KieSession session = kieContainer.newKieSession();
// Fact对象
Student student = new Student();
student.setAge(5);
// Fact对象插入工作内存
session.insert(student);
// 激活规则
session.fireAllRUles();
session.dispose();
rule "rule_name_1"
when
$student:Student(age < 10)
then
$student.setAge(15);
System.out.println("规则rule_name_1匹配成功");
end
rule "rule_name_2"
when
$student:Student(age > 10 && age < 20)
then
System.out.println("规则rule_name_2匹配成功");
end
// 规则执行效果
/* 控制台输出:
"规则rule_name_1匹配成功"
*/
rule "rule_name_1"
when
$student:Student(age < 10)
then
$student.setAge(15);
// update 方法用于更新Fact对象,更新完成后会导致相关规则重新匹配
update($student);
System.out.println("规则rule_name_1匹配成功");
end
rule "rule_name_2"
when
$student:Student(age > 10 && age < 20)
then
System.out.println("规则rule_name_2匹配成功");
end
// 规则执行效果
/* 控制台输出:
"规则rule_name_1匹配成功"
"规则rule_name_2匹配成功"
*/
rule "rule_name_1"
when
$student:Student(age < 10)
then
$student.setAge(15);
// update 方法用于更新工作内存中的Fact对象,更新完成后会导致相关规则重新匹配
update($student);
System.out.println("规则rule_name_1匹配成功");
end
rule "rule_name_2"
when
$student:Student(age > 10 && age < 20)
then
$student.setAge(25);
update($student);
System.out.println("规则rule_name_2匹配成功");
end
rule "rule_name_3"
when
$student:Student(age > 20 && age < 30)
then
System.out.println("规则rule_name_3匹配成功");
end
// 规则执行效果
/* 控制台输出:
"规则rule_name_1匹配成功"
"规则rule_name_2匹配成功"
"规则rule_name_3匹配成功"
*/
使用update方法,在更新数据时,要注意防止发生死循环。
-
insert方法
insert方法的作用是向工作内存中插入数据,并让相关的规则重新匹配。
rule "rule_name_0"
when
$student:Student(age == 10)
then
Student student = new Student();
student.setAge(5);
// insert 方法用于向工作内存中插入Fact对象,会导致相关规则重新匹配
insert(student);
System.out.println("规则rule_name_0匹配成功");
end
rule "rule_name_1"
when
$student:Student(age < 10)
then
$student.setAge(15);
// update 方法用于更新工作内存中的Fact对象,更新完成后会导致相关规则重新匹配
update($student);
System.out.println("规则rule_name_1匹配成功");
end
rule "rule_name_2"
when
$student:Student(age > 10 && age < 20)
then
$student.setAge(25);
update($student);
System.out.println("规则rule_name_2匹配成功");
end
rule "rule_name_3"
when
$student:Student(age > 20 && age < 30)
then
System.out.println("规则rule_name_3匹配成功");
end
// 规则执行效果
/* 控制台输出:
"规则rule_name_0匹配成功"
"规则rule_name_1匹配成功"
"规则rule_name_2匹配成功"
"规则rule_name_3匹配成功"
*/
-
retract方法
retract方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。
rule "rule_name_0" salience 10 //属性salience 配置规则执行优先级,数值越大优先级越高 when $student:Student(age == 10) then // retract 方法用于删除工作内存中的Fact对象,会导致相关规则重新匹配 retract(student); System.out.println("规则rule_name_0匹配成功"); end rule "rule_name_1" when $student:Student(age == 10) then Student student = new Student(); student.setAge(5); // insert 方法用于向工作内存中插入Fact对象,会导致相关规则重新匹配 insert(student); System.out.println("规则rule_name_1匹配成功"); end rule "rule_name_2" when $student:Student(age < 10) then $student.setAge(15); // update 方法用于更新工作内存中的Fact对象,更新完成后会导致相关规则重新匹配 update($student); System.out.println("规则rule_name_2匹配成功"); end rule "rule_name_3" when $student:Student(age > 10 && age < 20) then $student.setAge(25); update($student); System.out.println("规则rule_name_3匹配成功"); end rule "rule_name_4" when $student:Student(age > 20 && age < 30) then System.out.println("规则rule_name_4匹配成功"); end // 规则执行效果 /* 控制台输出: "规则rule_name_0匹配成功" */
规则属性
属性名 | 说明 |
---|---|
salience | 指定规则执行优先级 |
dialect | 指定规则使用的语言类型,取值为java和mvel |
enabled | 指定规则是否启用 |
date-effective | 指定规则生效时间 |
date-expires | 指定规则失效时间 |
activation-group | 激活分组,具有相同分组名称的规则只能有一个规则触发 |
agenda-group | 议程分组,只有回去焦点的组中的规则才有可能触发 |
timer | 定时器,指定规则触发的时间 |
auto-focus | 自动获取焦点,一般结合agenda-group一起使用 |
no-loop | 防止死循环 |
-
enabled属性
enabled属性对应的取值为true或false,默认值为true
用于指定当前规则是否启用,如果设置的值为false,则当前规则无论是否匹配成功都不会触发
Student student = new Student();
student.setAge(20);
session.insert(student);
session.fireAllRules();
session.dispose();
rule "rule_name"
enabled false
when
$student:Student(age > 10)
then
System.out.println("规则rule_name匹配成功");
end
// 规则执行效果
/* 控制台输出: (无输出)
*/
-
dialect属性
dialect属性用于指定当前规则使用的语言类型,取值为java和mvel,默认值为java
mvel像正则表达式一样,有直接支持集合、数组和字符串匹配的操作符
mvel还提供了用来配置和构造字符串的模板语言
mvel表达式内容包括属性表达式、布尔表达式、方法调用、变量赋值、函数定义等
-
salience属性
salience属性用于指定规则的执行优先级,取值类型为Integer,数值越大越优先执行
每个规则都有一个默认的执行顺序,如果不设置salience属性,规则体的执行顺序为由上到下
package test_salience
rule "rule_1"
salience 10
when
eval(true) // 返回true,即当前规则匹配成功
then
System.out.println("规则rule_1匹配成功");
end
rule "rule_2"
salience 11
when
eval(true) // 返回true,即当前规则匹配成功
then
System.out.println("规则rule_2匹配成功");
end
rule "rule_3"
salience 5
when
eval(true) // 返回true,即当前规则匹配成功
then
System.out.println("规则rule_3匹配成功");
end
// 规则执行效果
/* 控制台输出:
"规则rule_2匹配成功"
"规则rule_1匹配成功"
"规则rule_3匹配成功"
*/
-
no-loop属性
no-loop属性用于防止死循环,当规则通过update之类的函数修改了Fact对象时,可能使当前规则再次被激活从而导致死循环
no-loop属性取值类型为Boolean,默认值为false
rule "rule_name"
when
$student:Student(age == 10)
then
update($student);
System.out.println("规则rule_name匹配成功");
end
// 产生死循环
rule "rule_name"
no-loop true
when
$student:Student(age == 10)
then
update($student);
System.out.println("规则rule_name匹配成功");
end
// 解决死循环
-
activation-group属性
activation-group属性是指激活分组,取值为String类型,具有相同分组名曾的规则只能有一个规则被触发 (按照执行优先级进行触发)
package test_activation_group rule "rule_1" activation-group "myGroup" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_1匹配成功"); end rule "rule_2" activation-group "myGroup" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_2匹配成功"); end // 规则执行效果 /* 控制台输出: "规则rule_1匹配成功" */
-
agenda-group属性
agenda-group属性,议程分组,属于另一种可控的规则执行方式。用户可以通过设置agenda-group来控制规则的执行,只有获取焦点的组中的规则才会被触发
// 指定组获得焦点 session.getAgenda().getAgendaGroup("agenda_group_1").setFocus(); session.fireAllRules(); session.dispose();
package test_agenda_group rule "rule_1" agenda-group "agenda_group_1" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_1匹配成功"); end rule "rule_2" agenda-group "agenda_group_1" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_2匹配成功"); end rule "rule_3" agenda-group "agenda_group_2" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_3匹配成功"); end rule "rule_4" agenda-group "agenda_group_2" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_4匹配成功"); end // 规则执行效果 /* 控制台输出: "规则rule_1匹配成功" "规则rule_2匹配成功" */
-
auto-focus属性
auto-focus属性为自动获取焦点,取值类型为Boolean,默认值为false
一般结合agenda-group属性使用,当一个议程分组未获取焦点时,可以设置auto-focus属性来控制
// 指定组获得焦点 session.getAgenda().getAgendaGroup("agenda_group_1").setFocus(); session.fireAllRules(); session.dispose();
package test_agenda_group rule "rule_1" agenda-group "agenda_group_1" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_1匹配成功"); end rule "rule_2" agenda-group "agenda_group_1" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_2匹配成功"); end rule "rule_3" agenda-group "agenda_group_2" auto-focus true when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_3匹配成功"); end rule "rule_4" agenda-group "agenda_group_2" // auto-focus true when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_4匹配成功"); end // 同属于一个agenda-group的其中任意一个规则设置了auto-foucs=true,那么整个该对应的agend-group 将自动获取焦点 // 规则执行效果 /* 控制台输出: "规则rule_1匹配成功" "规则rule_2匹配成功" "规则rule_3匹配成功" "规则rule_4匹配成功" */
-
timer属性
timer属性可以通过定时器的方式指定规则执行的时间,使用方式有两种
方式一
timer(int: <initial delay> <repeat interval>?)
此种方式遵循 对象的使用方式,第一个参数表示几秒之后执行,第二个参数表示每隔几秒执行一次,第二个参数可选
方式二
timer(cron: <cron expression>)
此种方式使用标准的 表达式的使用方式来定义规则执行的时间
KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieService.newKieClasspathContainer(); final KieSession session = kieContainer.newKieSession(); new Thread (new Runnable (){ public void run(){ // 启动规则引擎进行规则匹配,直到调用halt方法才结束规则引擎 session.fireUnitlHalt(); } }).start(); Thread.sleep(10000); // 指定持续10s // 结束规则引擎 session.halt(); // 关闭会话 session.dispose();
package test_timer // timer 第一种使用方式 rule "rule_1" timer(3s 2s) // 表示 3s后触发,每隔2s 触发一次 when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_1匹配成功"); end // 规则执行效果 /* 控制台输出: "规则rule_1匹配成功" "规则rule_1匹配成功" "规则rule_1匹配成功" "规则rule_1匹配成功" */
package test_timer import java.util.Date import java.text.SimpleDateFormat // timer 第二种使用方式 rule "rule_1" timer(cron:0/1 * * * * ?) // 表示 每隔1s触发一次 when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_1匹配成功,触发的时间为:" + newSimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); end // 规则执行效果 /* 控制台输出: "规则rule_1匹配成功,触发的时间为:201-06-03 16:33:06" "规则rule_1匹配成功,触发的时间为:201-06-03 16:33:08" "规则rule_1匹配成功,触发的时间为:201-06-03 16:33:10" "规则rule_1匹配成功,触发的时间为:201-06-03 16:33:12" "规则rule_1匹配成功,触发的时间为:201-06-03 16:33:14" */
-
date-effective属性
date-effective属性用于指定规则的生效时间,即只有当前系统时间大于等于设置的时间或日期规则才有可能触发
默认日期格式为: ,用户也可以自定义日期格式
//设置日期格式 System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss"); session.fireAllRules(); session.dispose();
package test_date_effective import java.util.Date import java.text.SimpleDateFormat rule "rule_1" date-effective "2021-06-03 17:00:00" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_1匹配成功,触发的时间为:" + newSimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); end // 规则执行效果 /* 控制台输出: "规则rule_1匹配成功,触发的时间为:201-06-03 17:00:00" */
-
date-expires属性
date-expires属性用于指定规则的失效时间,即只有当前系统时间小于设置的时间或日期规则才有可能触发
默认日期格式为: ,用户也可以自定义日期格式
//设置日期格式 System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss"); session.fireAllRules(); session.dispose();
package test_date_expires import java.util.Date import java.text.SimpleDateFormat rule "rule_1" date-expires "2021-06-03 18:00:00" when eval(true) // 返回true,即当前规则匹配成功 then System.out.println("规则rule_1匹配成功,触发的时间为:" + newSimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); end // 规则执行效果 /* 控制台输出: "规则rule_1匹配成功,触发的时间为:201-06-03 17:00:00" */
Drools高级语法
global全局变量
global关键字用在规则文件中定义全局变量,它可以让应用程序的对象在规则文件中能够被访问,可以用来为规则文件提供数据或服务
语法结构为:
global 对象类型 对象名称
在使用global定义的全局变量时需要注意两点:
- 如果对象类型为包装类型时,在一个规则中改变了global的值,那么只针对当前规则有效,对其他规则中的global不会有影响。可以理解为它是当前规则代码中的global副本,规则内部修改不会影响全局的使用
- 如果对象类型为集合类型或JavaBean时,在一个规则中改变了global的值,对java代码和所有规则都有效
包装类型实例:
// 设置全局变量,变量名称必须与规则文件中定义的变量名保持一致
session.setGlobal("count", 5);
session.fireAllRules();
session.dispose();
package test_global
// 定义一个包装类型的全局变量
global java.lang.Integer count
rule "rule_1"
when
eval(true) // 返回true,即当前规则匹配成功
then
count += 10
System.out.println("规则rule_1匹配成功");
System.out.println("全局变量count的值为:" + count);
end
rule "rule_2"
when
eval(true) // 返回true,即当前规则匹配成功
then
System.out.println("规则rule_2匹配成功");
System.out.println("全局变量count的值为:" + count);
end
// 规则执行效果
/* 控制台输出:
"规则rule_1匹配成功"
"全局变量count的值为:15"
"规则rule_2匹配成功"
"全局变量count的值为:5"
*/
集合类型实例:
// 设置全局变量,变量名称必须与规则文件中定义的变量名保持一致
List<String> list = new ArrayList<String>();
list.add("java");
session.setGlobal("list", list);
session.fireAllRules();
System.out.println("Java程序中全局变量list的size为:" + list.size());
session.dispose();
package test_global
// 定义一个集合类型的全局变量
global java.util.List list
rule "rule_1"
when
eval(true) // 返回true,即当前规则匹配成功
then
list.add("Hello World!")
System.out.println("规则rule_1匹配成功");
System.out.println("全局变量list的size为:" + list.size());
end
rule "rule_2"
when
eval(true) // 返回true,即当前规则匹配成功
then
System.out.println("规则rule_2匹配成功");
System.out.println("全局变量list的size为:" + list.size());
end
// 规则执行效果
/* 控制台输出:
"规则rule_1匹配成功"
"全局变量list的size为:2"
"规则rule_2匹配成功"
"全局变量list的size为:2"
"Java程序中全局变量list的size为:2"
*/
query查询
query查询提供了一种 查询Working Memory中符合约束条件的Fact对象 的简单方法
它仅包含规则文件中的LHS部分,不用指定 when 和 then 部分并且以 end结束
语法结构如下:
query 查询的名称 (可选参数)
LHS
end
不带查询参数
Student s1 = new Student();
Student s2 = new Student();
s1.setAge(15);
s2.setAge(25);
session.insert(s1);
session.insert(s2);
// 根据名称调用规则文件中定义的查询
QueryResults result = session.getQueryResults("query_1");
System.out.println("执行query,符合条件的Fact对象个数:" + result.size());
// 获取查询对象
for(QueryResultsRow row:QueryResults){
Student student = (Student) row.get("$student");
System.out.println(student.getAge());
}
session.dispose();
package test_query
import com.item.drools.entity.Student
query "query_1"
$student: Student(age > 20)
end
// 规则执行效果
/* 控制台输出:
"执行query,符合条件的Fact对象个数:1"
25
*/
带有查询参数
Student s1 = new Student();
Student s2 = new Student();
s1.setAge(30);
s1.setName("Tom");
s2.setAge(25);
s2.setName("Leo");
session.insert(s1);
session.insert(s2);
// 根据名称调用规则文件中定义的查询
QueryResults result = session.getQueryResults("query_1", "Leo");
System.out.println("执行query,符合条件的Fact对象个数:" + result.size());
// 获取查询对象
for(QueryResultsRow row:QueryResults){
Student student = (Student) row.get("$student");
System.out.println(student.getName() + "的年龄为:" + student.getAge());
}
session.dispose();
package test_query
import com.item.drools.entity.Student
query "query_1"(String studentName)
$student: Student(age > 20 name == studentName)
end
// 规则执行效果
/* 控制台输出:
"执行query,符合条件的Fact对象个数:1"
"Leo的年龄为:25"
*/
function函数
function关键字用于在规则文件中定义函数,就相当于java类中的方法
可以在规则体中调用定义的函数,使用函数的好处是可以将业务逻辑集中放置在一个地方,根据需要可以对函数进行修改
语法结构:
function 返回值类型 函数名(可选参数){
// 逻辑代码
}
//Fact对象
Student student = new Student();
student.setAge(26);
student.setName("Leo");
session.insert(student);
session.fireAllRules();
session.dispose();
package test_function
import com.item.drools.entity.Student
//定义函数
function String sayHello(String name){
return "Hello" + name;
}
rule "rule_1"
when
$student:Student(age > 25)
then
System.out.println("规则rule_1匹配成功");
//调用定义的函数
String res = sayHello($student.getName());
System.out.println("调用sayHello函数,返回结果为:" + res);
end
// 规则执行效果
/* 控制台输出:
规则rule_1匹配成功
调用sayHello函数,返回结果为:Hello Leo
*/
LHS加强
在规则体中,LHS部分是介于when和then之间的部分,主要用于模式匹配,只有匹配结果为true时,才会触发RHS部分执行
- 复合值限制 in/not in
复合值限制是指超过一种匹配值的限制条件,类似于SQL语句中的in关键字
Drools规则体中的LHS部分可以使用in或者not in进行复合值的匹配
语法结构:
Object(field in (比较值1, 比较值2,.....))
// 示例
$student:Student(name in ("张三", "李四", "王五"))
$student:Student(name not in ("张三", "李四", "王五"))
- 条件元素eval
eval用于规则体的LHS部分,并返回一个Boolean类型的值
语法结构:
eval(表达式)
// 示例
eval(true) // 返回true
eval(false) // 返回false
eval(1 == 1) // 返回true
- 条件元素not
条件元素not用于判断Working Memory中是否存在某个Fact对象,如果不存在则返回true,如果存在则返回false
语法结构:
not Object(可选属性约束)
// 示例
not Student()
not Student(age < 10)
- 条件元素exists
条件元素exists的作用与条件元素not相反,用于判断Working Memory中是否存在某个Fact对象,如果存在则返回true,如果不存在则返回false
语法结构:
exists Object(可选属性约束)
// 示例
exists Student()
exists Student(age < 10 && name != null)
两者的区别:当向Working Memory中加入多个满足条件的Fact对象时,使用了exists的规则执行一次,而不使用exists的规则会执行多次
session.insert(new Student());
session.insert(new Student());
session.fireAllRules();
session.dispose();
package test_exists
import com.item.drools.entity.Student
// 只执行一次
rule "rule_1"
when
exists Student()
then
System.out.println("规则rule_1匹配成功");
end
// 当Working Memory中存在多个Fact对象时,执行多次
rule "rule_2"
when
Student()
then
System.out.println("规则rule_2匹配成功");
end
// 规则执行效果
/* 控制台输出:
规则rule_1匹配成功
规则rule_2匹配成功
规则rule_2匹配成功
*/
- 规则继承extend
规则之间可以使用extends关键字进行规则条件部分的继承,类似于java类之间的继承
Student student = new Student();
student.setAge(20);
session.insert(student);
session.fireAllRules();
session.dispose();
package test_function
import com.item.drools.entity.Student
rule "rule_1"
when
Student(age > 25)
then
System.out.println("规则rule_1匹配成功");
end
// 规则LHS部分继承
rule "rule_2" extends "rule_1"
when
Student(age < 50) // 等价于 Student(age > 25 && age < 50)
then
System.out.println("规则rule_2匹配成功");
end
// 规则执行效果
/* 控制台输出:
null
*/
RHS加强
RHS部分是规则体的重要组成部分,当LHS部分的条件匹配成功后,对应的RHS部分就会触发执行
一般在RHS部分中需要进行业务处理
- halt
halt方法的作用是立即终止后面所有规则的执行
package test_halt
rule "rule_1"
when
eval(true)
then
System.out.println("规则rule_1匹配成功");
// 立即终止后面所有规则的执行
drools.halt();
end
rule "rule_2"
when
eval(true)
then
System.out.println("规则rule_2匹配成功");
end
// 规则执行效果
/* 控制台输出:
规则rule_1匹配成功
*/
- getWorkingMemory
getWorkingMemory方法的作用是返回工作内存对象
package test_get_working_memory
rule "rule_1"
when
eval(true)
then
System.out.println("规则rule_1匹配成功");
// WorkingMemory workingMemory = drools.getWorkingMemory();
System.out.println(drools.getWorkingMemory());
end
// 规则执行效果
/* 控制台输出:
规则rule_1匹配成功
KieSession[0]
*/
- getRule
getRule方法的作用是返回规则对象
package test_get_rule
rule "rule_1"
when
eval(true)
then
System.out.println("规则rule_1匹配成功");
System.out.println(drools.getRule());
end
// 规则执行效果
/* 控制台输出:
规则rule_1匹配成功
[RUle name=rule_1, agendaGroup=MAIN, salience=0, no-loop=false]
*/
SpringBoot 整合 Drools
-
创建工程,并配置pom.xml
<dependencies> <!-- drools 规则引擎 --> <dependency> <groupId>org.drools</groupId> <artifactId>drools-core</artifactId> <version>7.6.0.Final</version> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-compiler</artifactId> <version>7.6.0.Final</version> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-templates</artifactId> <version>7.6.0.Final</version> </dependency> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>7.6.0.Final</version> </dependency> <dependency> <groupId>org.kie</groupId> <artifactId>kie-spring</artifactId> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> </exclusions> <version>7.6.0.Final</version> </dependency> </dependencies>
-
创建/resources/application.yml文件或者修改/resources/application.approperties文件
server: port: 8080 spring: application: name: drools_springboot
-
创建规则文件/resources/rules/helloworld.drl
package helloworld rule "hello_world" when eval(true) then System.out.println("规则hello_world触发") end
-
编写配置类DroolsConfig (配置文件)
package com.item.drools.config; /* 规则引擎配置类 */ @Configuration Public class DroolsConfig{ //指定配置规则存放的目录 private static final String RULES_PATH = "rules/"; private final KieServices kieServices = KieServices.Factory.get(); @Bean @ConditionalOnMissingBean public KieFileSystem kieFileSystem() throws IOException(){ KieFileSyste, kieFileSystem = kieServices.newKieFileSystem(); ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); Resource[] files = resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "*.drl"); String path = null; for(Resoure file: files){ path = RULE_PATH + file.getFileName(); kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8")); } } }
-
创建RuleServices类
-
创建Controller