Drools基础语法

814 阅读7分钟

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:规则属性,是 rulewhen 之间的参数,为可选项。例如 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

通过上面的例子可以知道,匹配的条件为:

  1. 工作内存中必须存在 Order 这种类型的Fact对象(类型约束)。
  2. Fact对象的 originalPrice 属性值必须小于200(属性约束)。
  3. 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 对象类型 对象名称

使用时需要注意:

  1. 如果对象类型为包装类型时,在一个规则中改变了 global 的值,那么只针对当前规则有效,对其他规则中的 global 不会有影响。可以理解为它是当前规则代码中的 global 副本,规则内部修改不会影响全局的使用。
  2. 如果对象类型为集合类型或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"