Drools基础语法
一、Drools简介
Drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。其官网地址为:drools.org/ ,源码下载地址为:github.com/kiegroup/dr… 。
二、规则文件构成
在使用Drools时,非常重要的一个工作就是编写规则文件,通常规则文件的后缀为 .drl(Drools Rule Language的缩写)。一套完整的规则文件内容构成如下:
| 关键字 | 描述 |
|---|---|
| package | 包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用 |
| import | 用于导入类或者静态方法 |
| global | 全局变量 |
| function | 自定义函数 |
| query | 查询 |
| rule end | 规则体 |
此外,Drools支持的规则文件除了 .drl 形式,还有Excel文件类型。
以下是一个规则文件的基本框架示例:
package rules
imports
globals
functions
queries
rules
其中,package 必须在第一行申明,其他的元素顺序无关,并且是可选的。
三、规则体语法结构
规则体是规则文件内容中的重要组成部分,是进行业务规则判断、处理业务结果的部分。其语法结构如下:
rule "ruleName"
attributes
when
LHS
then
RHS
end
各关键字说明
- rule:关键字,表示规则开始,参数为规则的唯一名称。
- attributes:规则属性,是
rule与when之间的参数,为可选项。例如no-loop属性可以防止规则循环触发。 - when:关键字,后面跟规则的条件部分。
- LHS(Left Hand Side) :是规则的条件部分的通用名称。它由零个或多个条件元素组成。如果LHS为空,则它将被视为始终为true的条件元素。
- then:关键字,后面跟规则的结果部分。
- RHS(Right Hand Side) :是规则的后果或行动部分的通用名称。
- end:关键字,表示一个规则结束。
在同一个DRL的规则文件中必须具有唯一的规则名称,如果不同DRL规则中包含同一package包的相同规则名称,则后加载的规则会覆盖之前的。规则的LHS遵循 when 关键字(最好另起一行),同样RHS遵循的是 then 关键字(也另起一行)。该规则以关键字 end 结束,规则之间不能嵌套。
四、注释
在 .drl 形式的规则文件中使用注释和Java类中使用注释一致,分为单行注释和多行注释。
- 单行注释用
//进行标记,示例:
//规则rule1的注释,这是一个单行注释
rule "rule1"
when
then
System.out.println( "rule1触发" );
end
- 多行注释以
/*开始,以*/结束,示例:
/*
规则rule2的注释,
这是一个多行注释
*/
rule "rule2"
when
then
System.out.println( "rule2触发" );
end
五、Pattern模式匹配
Drools中的匹配器可以将规则库(Rule Base)中的所有规则与工作内存(Working Memory)中的Fact对象进行模式匹配,我们需要在规则体的LHS部分定义规则并进行模式匹配。LHS部分由一个或者多个条件组成,条件又称为 pattern。
pattern的语法结构
pattern 的语法结构为:绑定变量名:Object(Field约束)。其中绑定变量名可以省略,通常绑定变量名的命名一般建议以 $ 开始。如果定义了绑定变量名,就可以在规则体的RHS部分使用此绑定变量名来操作相应的Fact对象。Field约束部分是需要返回true或者false的0个或多个表达式。
示例
//规则二:所购图书总价在100到200元的优惠20元
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(属性约束)。
以上条件必须同时满足当前规则才有可能被激活。
绑定变量的使用
绑定变量既可以用在对象上,也可以用在对象的属性上。例如上面的例子可以改为:
//规则二:所购图书总价在100到200元的优惠20元
rule "book_discount_2"
when
$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
多个pattern的连接
LHS部分还可以定义多个 pattern,多个 pattern 之间可以使用 and 或者 or 进行连接,也可以不写,默认连接为 and。
//规则二:所购图书总价在100到200元的优惠20元
rule "book_discount_2"
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( "成功匹配到规则二:所购图书总价在100到200元的优惠20元" );
end
六、比较操作符
Drools提供的比较操作符如下表:
| 符号 | 说明 |
|---|---|
| 大于 | |
| < | 小于 |
| >= | 大于等于 |
| <= | 小于等于 |
| == | 等于 |
| != | 不等于 |
| contains | 检查一个Fact对象的某个属性值是否包含一个指定的对象值 |
| not contains | 检查一个Fact对象的某个属性值是否不包含一个指定的对象值 |
| memberOf | 判断一个Fact对象的某个属性是否在一个或多个集合中 |
| not memberOf | 判断一个Fact对象的某个属性是否不在一个或多个集合中 |
| matches | 判断一个Fact对象的属性是否与提供的标准的Java正则表达式进行匹配 |
| not matches | 判断一个Fact对象的属性是否不与提供的标准的Java正则表达式进行匹配 |
七、其他重要概念及语法
变量
Drools使用匹配的方式对Fact进行比对,可以指定变量来描述一个类型或者一个映射一个类的属性。使用 $Variable 来定义一个变量,例如:
$account : Account($type : type)
这里定义了两个变量,$account 表示定义一个类型为 Account 的变量,而 $type 映射 Account 类型中的 type 属性。定义变量是为了在后续的规则中使用。
全局变量(global)
global 关键字用于在规则文件中定义全局变量,它可以让应用程序的对象在规则文件中能够被访问,用来为规则文件提供数据或服务。
语法结构为:global 对象类型 对象名称。
使用时需要注意:
- 如果对象类型为包装类型时,在一个规则中改变了
global的值,那么只针对当前规则有效,对其他规则中的global不会有影响。可以理解为它是当前规则代码中的global副本,规则内部修改不会影响全局的使用。 - 如果对象类型为集合类型或JavaBean时,在一个规则中改变了
global的值,对java代码和所有规则都有效。
以下是一个示例:
// 规则文件
package testglobal
/*
此规则文件用于测试global全局变量
*/
global java.lang.Integer count //定义一个包装类型的全局变量
global com.itheima.drools.service.UserService userService //定义一个JavaBean类型的全局变量
global java.util.List gList //定义一个集合类型的全局变量
rule "rule_global_1"
when
then
count += 10; //全局变量计算,只对当前规则有效,其他规则不受影响
userService.save(); //调用全局变量的方法
gList.add( "itcast" ); //向集合类型的全局变量中添加元素,Java代码和所有规则都受影响
gList.add( "itheima" );
System.out.println( "count=" + count);
System.out.println( "gList.size=" + gList.size());
end
rule "rule_global_2"
when
then
userService.save();
System.out.println( "count=" + count);
System.out.println( "gList.size=" + gList.size());
end
查询(query)
query 查询提供了一种查询工作内存中符合约束条件的Fact对象的简单方法。它仅包含规则文件中的LHS部分,不用指定 “when” 和 “then” 部分并且以 end 结束。具体语法结构如下:
query 查询的名称(可选参数)
LHS
end
以下是一个示例:
// 规则文件
package testquery
import com.itheima.drools.entity.Student
/*
此规则文件用于测试query查询
*/
// 不带参数的查询
// 当前query用于查询Working Memory中age>10的Student对象
query "query_1"
$student: Student(age > 10)
end
// 带有参数的查询
// 当前query用于查询Working Memory中age>20同时name需要和传递的参数name相同的Student对象
query "query_2"( String sname)
$student: Student(age > 20 && name == sname)
end
函数(function)
function 可以在规则文件中定义,但更多的是使用外部类中定义的静态方法,这样只要Java中可以实现的逻辑,在规则中都可以做为 function 调用。
调用外部类的 function 需要注意的是方法必须是静态的(static),而且这个类可以在辅助类中定义。外部类需要 import,此时在 function 中用到的参数类型也需要 import。
例如,在外部的 ValidationHelper 辅助类中定义一个静态方法:
public static double calculateAccount(Account account) {
return 100 + account.balance * 1.2;
}
在规则 .drl 文件中可以这么使用:
import com.kingsun.drools.domain.Account;
import function com.kingsun.drools.util.ValidationHelper.calculateAccount;
rule "validation account"
when
$account : Account(balance > 100)
then
Account(balance == calculateAccount($account));
end
方言(dialect)
在规则表达式中可以使用方言来简化表达式,使之更加具有可读性。方言默认的是Java,Drools也支持 mvel,在 package 的后面声明该规则文件使用的方言。
mvel 是一种基于Java应用程序的表达式语言,它支持属性和方法的直接访问。示例:
package com.kingsun.drools.rules;
dialect "mvel"