1 规则文件的组成
在Drools的初体验里面,我们编写了一个droolsFirst.drl的规则文件,并运行达到了预期的结果。从规则的命名可以看出,Drools的规则文件一般是以.drl(Drools Rule Language)结尾的文件。
一个完整的规则文件一般会由下面内容构成:
注意:上面说的Drools规则文件一般是以.drl结尾的,其实在实际的使用中还可以使用到决策表(.xls,.xlsx)结尾的文件,当然也可以把规则预先存到mysql等数据库中,使用的时候加载出来。
2 规则体语法结构
规则体是规则文件内容中的重要组成部分,是进行业务规则判断、处理业务结果的重要组成部分。
构成规则的规则体语法结构如下:
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:关键字,表示一个规则结束。
3 Pattern模式匹配
在介绍Pattern模式匹配之前,我们先弄清楚一个原理。Drools规则引擎的运行原理是将Working Memory中的Fact对象匹配编写的规则,如果匹配成功就会触发相应的逻辑。在编写规则的LHS部分就是由多个条件构成的,整个过程的执行就是条件的判断和匹配。
我们通过上面编写的例子来看看这种方式:
/**
* 规则名称
**/
rule "rule_monthIn_2"
when
/**
* $s 绑定变量,如果后面条件匹配成功,用来接收Fact中的对象
* PersonBasicInfo 类型匹配,工作内存中要有一个PersonBasicInfo类型的对象
* monthIn 属性匹配,如java中的属性值,这里我们可以看到用的是收入大小的比较
**/
$s:PersonBasicInfo(monthIn.compareTo(BigDecimal.valueOf(10000))<0 && monthIn.compareTo(BigDecimal.valueOf(5000))>=0)
then
Integer score=Objects.isNull($s.getScore())?0+5:$s.getScore()+5;
$s.setScore(score);
end
通过上面的例子,可以看出只有同时满足工作内存中有一个PersonBasicInfo的对象,并且这个对象中的月收入的范围在5000之10000之间才会满足条件出发then后面的逻辑。
绑定变量除了用来绑定对象以外,也可以用来在绑定属性,如下:
/**
* 规则名称
**/
rule "rule_monthIn_3"
when
/**
* $s 绑定变量,如果后面条件匹配成功,用来接收Fact中的对象
* PersonBasicInfo 类型匹配,工作内存中要有一个PersonBasicInfo类型的对象
* monthIn 属性匹配,如java中的属性值,这里我们可以看到用的是收入大小的比较
* $m,$m1用来绑定属性匹配的值(注意是指结果值,如果传入的monthIn是6000那么$m是-1,$m1是1)
**/
$s:PersonBasicInfo($m:monthIn.compareTo(BigDecimal.valueOf(10000))<0 && $m1:monthIn.compareTo(BigDecimal.valueOf(5000))>=0)
then
System.out.println($m);
System.out.println($m1);
end
LHS部分也可以写多个条件,多个条件可以用and连接在一起,不写默认是and。
4 比较运算符
Drools规则引擎提供的运算符除了有大小比较外还提供了正则匹配及集合的包含操作等,具体的运算符内容如下:
前面的比较运算符和Java中的比较运算符使用方式一致,下面我们重点了解一下集合匹配的一些相关操作。在介绍下面的案例之前,我们先描述一下这样一个例子,学生可以选择多种课程,现在要知道一个学生是否报了语文课或者要知道这个学生的名字是否姓张,其实这些操作就可以通过下面讲解的部分解决,我们先定义一个学生的类。
package com.dream21th.first;
import java.util.List;
/*
* @Author dream21th
**/
public class Student {
/**
* 学生姓名
*/
private String name;
/**
* 学生年龄
*/
private String age;
/**
* 课程
*/
private List<String> subjects;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public List<String> getSubjects() {
return subjects;
}
public void setSubjects(List<String> subjects) {
this.subjects = subjects;
}
}
4.1 contains和not contains
接下来编写一个Drools规则文件,文件的详细内容如下:
package ruls;
import com.dream21th.first.Student
/**
* 这个规则是判断包含的逻辑,如果选择的课程里面有语文课就会触发
**/
rule "rule_contains"
when
$s:Student(subjects contains "语文")
then
System.out.println("学生:"+$s.getName()+"选择的课程包含语文");
end
/**
* 这个规则是判断不包含的逻辑,如果选择的课程里面没有英语课就会触发
**/
rule "rule_not_contains"
when
$s:Student(subjects not contains "英语")
then
System.out.println("学生:"+$s.getName()+"选择的课程不包含英语");
end
编写一个测试用例,测试用例的代码如下:
@Test
public void test2(){
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer =kieServices.getKieClasspathContainer();
//会话对象,用于和规则引擎交互
KieSession kieSession = kieClasspathContainer.newKieSession();
Student student=new Student();
student.setName("马芸");
student.setAge(19);
student.setSubjects(Arrays.asList("语文","数学","英语"));
kieSession.insert(student);
//激活规则引擎,如果规则匹配成功则执行规则
kieSession.fireAllRules();
//关闭会话
kieSession.dispose();
}
这个例子的学生选择了"语文","数学","英语"三门课程,按照上面写的规则,只会触发第一条,运行代码得出结果和预期一致。
4.2 memberOf和not memberOf
接下来编写一个Drools规则文件,文件的详细内容如下:
package ruls;
import com.dream21th.first.Student
/**
* 这个规则判断是否是集合中的成员逻辑,如果选择的课程里面有语文课就会触发
**/
rule "rule_memberOf"
when
$s:Student("语文" memberOf subjects)
then
System.out.println("学生:"+$s.getName()+"语文在选择的课程中");
end
/**
* 这个规则判断不是集合中的成员的逻辑,如果选择的课程里面没有地理课就会触发
**/
rule "rule_not_memberOf"
when
$s:Student("地理" not memberOf subjects)
then
System.out.println("学生:"+$s.getName()+"地理不在选择的课程中");
end
编写一个测试用例,测试用例的代码如下:
@Test
public void test3(){
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer =kieServices.getKieClasspathContainer();
//会话对象,用于和规则引擎交互
KieSession kieSession = kieClasspathContainer.newKieSession();
Student student=new Student();
student.setName("徐佳莹");
student.setAge(21);
student.setSubjects(Arrays.asList("语文","数学","英语"));
kieSession.insert(student);
//激活规则引擎,如果规则匹配成功则执行规则
kieSession.fireAllRules();
//关闭会话
kieSession.dispose();
}
这个例子的学生选择了"语文","数学","英语"三门课程,按照上面写的规则,两条规则都会触发,运行代码得出结果和预期一致。
4.3 matches和not matches
matches和not matches是用来正则匹配逻辑的,接下来我们通过编写如下规则代码:
package ruls;
import com.dream21th.first.Student
/**
* 这个规则是匹配逻辑,如果学生姓名以赵开头就会触发规则
**/
rule "rule_matches"
when
$s:Student(name matches "赵.*")
then
System.out.println("学生:"+$s.getName()+"触发matches规则");
end
/**
* 如果学生姓名不是以云结尾就触发
**/
rule "rule_not_matches"
when
$s:Student(name not matches ".*云")
then
System.out.println("学生:"+$s.getName()+"触发not matches规则");
end
编写一个测试用例,测试用例的代码如下:
@Test
public void test4(){
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer =kieServices.getKieClasspathContainer();
//会话对象,用于和规则引擎交互
KieSession kieSession = kieClasspathContainer.newKieSession();
Student student=new Student();
student.setName("赵鹏程");
student.setAge(21);
kieSession.insert(student);
//激活规则引擎,如果规则匹配成功则执行规则
kieSession.fireAllRules();
//关闭会话
kieSession.dispose();
}
这个学生的姓名叫赵鹏程,满足上面的两个规则,就会触发编写的逻辑。
5 执行指定规则
通过上面运行的案例可以看到,我们在调用规则代码时,满足条件的规则都会被执行。那么如果我们只想执行其中的某个规则如何实现呢?Drools给我们提供的方式是通过规则过滤器来实现执行指定规则。对于规则文件不用做任何修改,只需要修改Java代码即可,如下:
//通过规则过滤器实现只执行指定规则
kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("规则名称"));
6 关键字
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
7 Drools内置方法
规则文件的RHS 部分的主要作用是通过一些内置方法来插入,删除或修改工作内存中的Fact数据,来达到控制规则引擎执行的目的。Drools提供了一些方法可以用来操作工作内存中的数据,操作完成后规则引擎会重新进行相关规则的匹配,原来没有匹配成功的规则在我们修改数据完成后有可能就会匹配成功了。
7.1 update方法使用
update方法的主要作用是将工作内存中的对象更新,进而可以匹配新的规则属性。我们编写新的规则文件,规则文件的路径是 \resources\first\updateTests.drl ,规则文件的内容如下:
package rules;
import com.dream21th.first.LoanInfo;
rule "update_rank_A"
when
$s:LoanInfo(rank=="A")
then
System.out.println("用户名:"+$s.getName()+",评级:"+$s.getRank());
$s.setRank("B");
update($s);//更新工作内存中的对象
end
rule "update_rank_B"
when
$s:LoanInfo(rank=="B")
then
System.out.println("用户名:"+$s.getName()+",评级:"+$s.getRank());
end
规则文件中使用到的LoanInfo类的信息如下:
package com.dream21th.first;
/*
* @Author dream21th
**/
public class LoanInfo {
private String name;
private String rank;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRank() {
return rank;
}
public void setRank(String rank) {
this.rank = rank;
}
}
接着我们编写测试案例,测试案例的具体内容如下:
@Test
public void testUpdate(){
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer =kieServices.getKieClasspathContainer();
//会话对象,用于和规则引擎交互
KieSession kieSession = kieClasspathContainer.newKieSession();
LoanInfo loanInfo=new LoanInfo();
loanInfo.setName("李云飞");
loanInfo.setRank("A");
kieSession.insert(loanInfo);
//激活规则引擎,如果规则匹配成功则执行规则
kieSession.fireAllRules();
//关闭会话
kieSession.dispose();
}
案例解释:
1,首先我们在工作内存中添加一个LoanInfo的对象,对象的Rank值为A;
2,update_rank_A 这条规则匹配成功,接在在then里面修改了工作内存中的Rank值,并使用update方法更新工作内存中的值;
3,由于工作内存中的Rank值被修改为B,接着update_rank_B 规则触发。
注意:工作内存中的值修改后,要通过update方法更新才会使下面的匹配规则生效;同时要注意规则的编写,避免由于更新工作内存中的值导致规则重新匹配产生死循环。
7.2 insert方法使用
insert方法主要是向工作内存中插入对象,并让相关规则触发和匹配。编写新的规则,规则文件的路径为\resources\first\insertTests.drl 。规则文件的具体内容如下:
package rules;
import com.dream21th.first.LoanInfo;
rule "insert_rank_C"
when
$s:LoanInfo(rank=="C")
then
System.out.println("用户名:"+$s.getName()+",评级:"+$s.getRank());
LoanInfo loanInfo=new LoanInfo();
loanInfo.setName("赵云龙");
loanInfo.setRank("D");
insert(loanInfo);//向工作内存中插入新的对象
end
rule "insert_rank_D"
when
$s:LoanInfo(rank=="D")
then
System.out.println("用户名:"+$s.getName()+",评级:"+$s.getRank());
end
rule "insert_name_1"
when
$s:LoanInfo(name=="赵云龙")
then
System.out.println("规则insert_name_1:用户名:"+$s.getName()+",评级:"+$s.getRank());
end
rule "insert_name_2"
when
$s:LoanInfo(name=="李云飞")
then
System.out.println("规则insert_name_2:用户名:"+$s.getName()+",评级:"+$s.getRank());
end
接着我们编写测试案例,测试案例的具体内容如下:
@Test
public void testInsert(){
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer =kieServices.getKieClasspathContainer();
//会话对象,用于和规则引擎交互
KieSession kieSession = kieClasspathContainer.newKieSession();
LoanInfo loanInfo=new LoanInfo();
loanInfo.setName("李云飞");
loanInfo.setRank("C");
kieSession.insert(loanInfo);
//激活规则引擎,如果规则匹配成功则执行规则
kieSession.fireAllRules();
//关闭会话
kieSession.dispose();
}
案例解释:
1,测试案例中先向工作内存中插入一个LoanInfo对象,对象的name属性为李云飞,rank为C;
2,insert_rank_C 规则满足条件触发,触发后再then逻辑里面新插入对象LoanInfo,对象的name属性为赵云龙,rank为D;
3,此时工作内存中有两个LoanInfo对象,新加的对象满足insert_rank_D和insert_name_1规则,原始对象满足insert_name_2规则,故可以在控制台中输出四条规则的触发结果;
7.3 retract方法
retract方法主要是删除工作内存中的对象,并让相关规则触发和匹配。编写新的规则,规则文件的路径为\resources\first\retractTests.drl 。规则文件的具体内容如下:
package rules;
import com.dream21th.first.LoanInfo;
rule "retract_rank_E_1"
when
$s:LoanInfo(rank=="E")
then
System.out.println("规则:retract_rank_E_1:用户名:"+$s.getName()+",评级:"+$s.getRank());
retract($s);//删除工作内存中的对象
end
rule "retract_rank_E_2"
when
$s:LoanInfo(rank=="E")
then
System.out.println("规则:retract_rank_E_2:用户名:"+$s.getName()+",评级:"+$s.getRank());
end
接着我们编写测试案例,测试案例的具体内容如下:
@Test
public void testRetract(){
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer =kieServices.getKieClasspathContainer();
//会话对象,用于和规则引擎交互
KieSession kieSession = kieClasspathContainer.newKieSession();
LoanInfo loanInfo=new LoanInfo();
loanInfo.setName("张红");
loanInfo.setRank("E");
kieSession.insert(loanInfo);
//激活规则引擎,如果规则匹配成功则执行规则
kieSession.fireAllRules();
//关闭会话
kieSession.dispose();
}
案例解释:
1,我们在LoanInfo中插入一个LoanInfo对象,对象的name属性是张红,rank为E;
2,接着触发retract_rank_E_1规则,规则触发后通过retract删除工作内存中的LoanInfo对象,发现retract_rank_E_2没有触发。
|