【Drools二】打工人学习Drools基础语法

2020-11-24 15:39:35 浏览数 (1)

目录

  • 0.项目搭建
  • 1. 规则文件
    • 1.1 构成
    • 1.2规则体
    • 2.基础语法
    • 2.1 注释
    • 2.2 Pattern模式匹配
    • 2.3比较操作符
    • 2.3.1 实践
  • 2.3执行指定规则
  • 3.Drools内置方法
    • 3.1update方法
    • 3.2 insert方法
    • 3.3 retract方法
  • 4. 规则属性
    • 4.1 enabled属性
    • 4.2 dialect属性
    • 4.3 salience属性
    • 4.4 no-loop属性
    • 4.5 activation-group属性
    • 4.6 agenda-group属性
    • 4.7 auto-focus属性
    • 4.8 timer属性
    • 4.9 date-effective属性
    • 4.10 date-expires属性
  • 参考资料

0.项目搭建

学习语法之前先把项目结构搭建起来。使用IDE创建Maven项目,引入Drools依赖。上一篇文章中已经介绍一个实战,溪源在此篇文章再重复分享一下。 第一步:创建maven工程drools_quickstart并导入drools相关maven坐标

代码语言:javascript复制
<dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-core</artifactId>
      <version>${drools.version}</version>
    </dependency>

    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-compiler</artifactId>
      <version>${drools.version}</version>
    </dependency>

    <dependency>
      <groupId>org.kie</groupId>
      <artifactId>kie-spring</artifactId>
      <version>${drools.version}</version>
    </dependency>

    <dependency>
      <groupId>org.kie</groupId>
      <artifactId>kie-internal</artifactId>
      <version>${drools.version}</version>
    </dependency>

    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-templates</artifactId>
      <version>${drools.version}</version>
    </dependency>

    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-decisiontables</artifactId>
      <version>7.35.0.Final</version>
    </dependency>

第二步:根据drools要求创建resources/META-INF/kmodule.xml配置文件

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <!--
        name:指定kbase的名称,可以任意,但是需要唯一
        packages:指定规则文件的目录,需要根据实际情况填写,否则无法加载到规则文件
        default:指定当前kbase是否为默认
    -->
    <kbase name="myKbase1" packages="rules" default="true">
        <!--
            name:指定ksession名称,可以任意,但是需要唯一
            default:指定当前session是否为默认
        -->
        <ksession name="ksession-rule" default="true"/>
    </kbase>
</kmodule>

第三步:创建规则文件resources/rules/。 后面将规则文件放入该文件夹中。

1. 规则文件

1.1 构成

在使用Drools时非常重要的一个工作就是编写规则文件,通常规则文件的后缀为.drl。

drl是Drools Rule Language的缩写。在规则文件中编写具体的规则内容。

一套完整的规则文件内容构成如下:

关键字

描述

package

包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用

import

用于导入类或者静态方法

global

全局变量

function

自定义函数

query

查询

rule end

规则体

Drools支持的规则文件,除了drl形式,还有Excel文件类型(决策表)的。

1.2规则体

规则体是规则文件内容中的重要组成部分,是进行业务规则判断、处理业务结果的部分。

规则体语法结构如下:

代码语言:javascript复制
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:关键字,表示一个规则结束。

2.基础语法

2.1 注释

在drl形式的规则文件中使用注释和Java类中使用注释一致,分为单行注释和多行注释。

单行注释用//进行标记,多行注释以/*开始,以*/结束。如下示例:

代码语言:javascript复制
//规则rule1的注释,这是一个单行注释
rule "rule1"
    when
    then
        System.out.println("rule1触发");
end

/*
规则rule2的注释,
这是一个多行注释
*/
rule "rule2"
    when
    then
        System.out.println("rule2触发");
end

2.2 Pattern模式匹配

Drools中的匹配器可以将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,那么我们就需要在规则体的LHS部分定义规则并进行模式匹配。LHS部分由一个或者多个条件组成,条件又称为pattern。

pattern的语法结构为:

  1. $绑定变量名:Object (Field约束)
  • 其中绑定变量名可以省略,通常绑定变量名的命名一般建议以$开始。如果定义了绑定变量名,就可以在规则体的RHS部分使用此绑定变量名来操作相应的Fact对象。
  • Field约束部分是需要返回true或者false的0个或多个表达式。 例如:
代码语言:javascript复制
//规则二:所购图书总价在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------属性约束

以上条件必须同时满足当前规则才有可能被激活。

2.绑定变量既可以用在对象上,也可以用在对象的属性上。例如上面的例子可以改为:

代码语言:javascript复制
//规则二:所购图书总价在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

LHS部分还可以定义多个pattern,多个pattern之间可以使用and或者or进行连接,也可以不写,默认连接为and。

代码语言:javascript复制
//规则二:所购图书总价在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

2.3比较操作符

Drools提供的比较操作符,如下表:

符号

说明

>

大于

<

小于

>=

大于等于

<=

小于等于

==

等于

!=

不等于

contains

检查一个Fact对象的某个属性值是否包含一个指定的对象值

not contains

检查一个Fact对象的某个属性值是否不包含一个指定的对象值

memberOf

判断一个Fact对象的某个属性是否在一个或多个集合中

not memberOf

判断一个Fact对象的某个属性是否不在一个或多个集合中

matches

判断一个Fact对象的属性是否与提供的标准的Java正则表达式进行匹配

not matches

判断一个Fact对象的属性是否不与提供的标准的Java正则表达式进行匹配

前6个比较操作符和Java中的完全相同,下面我们重点学习后6个比较操作符。

语法

  • 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 “正则表达式”)

contain是前面包含后面,memberOf是后面包含前面

2.3.1 实践

第一步:创建实体类,用于测试比较操作符

代码语言:javascript复制
package com.itheima.drools.entity;
import java.util.List;

/**
 * 实体类
 * 用于测试比较操作符
 */
public class ComparisonOperatorEntity {
    private String names;
    private List<String> list;

    public String getNames() {
        return names;
    }

    public void setNames(String names) {
        this.names = names;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }
}

第二步:在/resources/rules下创建规则文件comparisonOperator.drl

代码语言:javascript复制
package comparisonOperator
import com.itheima.drools.entity.ComparisonOperatorEntity
/*
 当前规则文件用于测试Drools提供的比较操作符
*/

//测试比较操作符contains
rule "rule_comparison_contains"
    when
        ComparisonOperatorEntity(names contains "张三")
        ComparisonOperatorEntity(list contains names)
    then
        System.out.println("规则rule_comparison_contains触发");
end

//测试比较操作符not contains
rule "rule_comparison_notContains"
    when
        ComparisonOperatorEntity(names not contains "张三")
        ComparisonOperatorEntity(list not contains names)
    then
        System.out.println("规则rule_comparison_notContains触发");
end

//测试比较操作符memberOf
rule "rule_comparison_memberOf"
    when
        ComparisonOperatorEntity(names memberOf list)
    then
        System.out.println("规则rule_comparison_memberOf触发");
end

//测试比较操作符not memberOf
rule "rule_comparison_notMemberOf"
    when
        ComparisonOperatorEntity(names not memberOf list)
    then
        System.out.println("规则rule_comparison_notMemberOf触发");
end

//测试比较操作符matches
rule "rule_comparison_matches"
    when
        ComparisonOperatorEntity(names matches "张.*")
    then
        System.out.println("规则rule_comparison_matches触发");
end

//测试比较操作符not matches
rule "rule_comparison_notMatches"
    when
        ComparisonOperatorEntity(names not matches "张.*")
    then
        System.out.println("规则rule_comparison_notMatches触发");
end

第三步:编写单元测试

代码语言:javascript复制
//测试比较操作符
@Test
public void test3(){
    KieServices kieServices = KieServices.Factory.get();
    KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
    KieSession kieSession = kieClasspathContainer.newKieSession();

    ComparisonOperatorEntity comparisonOperatorEntity = new ComparisonOperatorEntity();
    comparisonOperatorEntity.setNames("张三");
    List<String> list = new ArrayList<String>();
    list.add("张三");
    list.add("李四");
    comparisonOperatorEntity.setList(list);

    //将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
    kieSession.insert(comparisonOperatorEntity);

    kieSession.fireAllRules();
    kieSession.dispose();
}

2.3执行指定规则

通过前面的案例可以看到,我们在调用规则代码时,满足条件的规则都会被执行。那么如果我们只想执行其中的某个规则如何实现呢?

Drools给我们提供的方式是通过规则过滤器来实现执行指定规则。对于规则文件不用做任何修改,只需要修改Java代码即可,如下:

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

ComparisonOperatorEntity comparisonOperatorEntity = new ComparisonOperatorEntity();
comparisonOperatorEntity.setNames("张三");
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
comparisonOperatorEntity.setList(list);
kieSession.insert(comparisonOperatorEntity);

//通过规则过滤器实现只执行指定规则
kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("rule_comparison_memberOf"));

kieSession.dispose();

3.Drools内置方法

规则文件的RHS部分的主要作用是通过插入,删除或修改工作内存中的Fact数据,来达到控制规则引擎执行的目的。Drools提供了一些方法可以用来操作工作内存中的数据,**操作完成后规则引擎会重新进行相关规则的匹配,**原来没有匹配成功的规则在我们修改数据完成后有可能就会匹配成功了。

创建如下实体类:

代码语言:javascript复制
package com.itheima.drools.entity;

import java.util.List;

/**
 * 学生
 */
public class Student {
    private int id;
    private String name;
    private int age;
    
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

3.1update方法

update方法的作用是更新工作内存中的数据,并让相关的规则重新匹配。 (要避免死循环)

第一步:编写规则文件/resources/rules/student.drl,文件内容如

代码语言:javascript复制
package student
import com.itheima.drools.entity.Student

/*
 当前规则文件用于测试Drools提供的内置方法
*/

rule "rule_student_age小于10岁"
    when
        $s:Student(age < 10)
    then
        $s.setAge(15);
        update($s);//更新数据,导致相关的规则会重新匹配
        System.out.println("规则rule_student_age小于10岁触发");
end

rule "rule_student_age小于20岁同时大于10岁"
    when
        $s:Student(age < 20 && age > 10)
    then
        $s.setAge(25);
        update($s);//更新数据,导致相关的规则会重新匹配
        System.out.println("规则rule_student_age小于20岁同时大于10岁触发");
end

rule "rule_student_age大于20岁"
    when
        $s:Student(age > 20)
    then
        System.out.println("规则rule_student_age大于20岁触发");
end

第二步:编写单元测试

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

Student student = new Student();
student.setAge(5);

//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
kieSession.insert(student);

kieSession.fireAllRules();
kieSession.dispose();

通过控制台的输出可以看到规则文件中定义的三个规则都触发了。

在更新数据时需要注意防止发生死循环。后面会讲到如何避免死循环即设置no-loop true

3.2 insert方法

insert方法的作用是向工作内存中插入数据,并让相关的规则重新匹配。

第一步:修改student.drl文件内容如下

代码语言:javascript复制
package student
import com.itheima.drools.entity.Student

/*
 当前规则文件用于测试Drools提供的内置方法
*/

rule "rule_student_age等于10岁"
    when
        $s:Student(age == 10)
    then
        Student student = new Student();
        student.setAge(5);
        insert(student);//插入数据,导致相关的规则会重新匹配
        System.out.println("规则rule_student_age等于10岁触发");
end

rule "rule_student_age小于10岁"
    when
        $s:Student(age < 10)
    then
        $s.setAge(15);
        update($s);
        System.out.println("规则rule_student_age小于10岁触发");
end

rule "rule_student_age小于20岁同时大于10岁"
    when
        $s:Student(age < 20 && age > 10)
    then
        $s.setAge(25);
        update($s);
        System.out.println("规则rule_student_age小于20岁同时大于10岁触发");
end

rule "rule_student_age大于20岁"
    when
        $s:Student(age > 20)
    then
        System.out.println("规则rule_student_age大于20岁触发");
end

第二步:编写单元测试

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

Student student = new Student();
student.setAge(10);

//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
kieSession.insert(student);

kieSession.fireAllRules();
kieSession.dispose();

通过控制台输出可以发现,四个规则都触发了,这是因为首先进行规则匹配时只有第一个规则可以匹配成功,但是在第一个规则中向工作内存中插入了一个数据导致重新进行规则匹配,此时第二个规则可以匹配成功。在第二个规则中进行了数据修改导致第三个规则也可以匹配成功,以此类推最终四个规则都匹配成功并执行了。

3.3 retract方法

retract方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。

第一步:修改student.drl文件内容如下

代码语言:javascript复制
package student
import com.itheima.drools.entity.Student

/*
 当前规则文件用于测试Drools提供的内置方法
*/

rule "rule_student_age等于10岁时删除数据"
    /*
    salience:设置当前规则的执行优先级,数值越大越优先执行,默认值为0.
    因为当前规则的匹配条件和下面规则的匹配条件相同,为了保证先执行当前规则,需要设置优先级
    */
    salience 100 
    when
        $s:Student(age == 10)
    then
        retract($s);//retract方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。
        System.out.println("规则rule_student_age等于10岁时删除数据触发");
end

rule "rule_student_age等于10岁"
    when
        $s:Student(age == 10)
    then
        Student student = new Student();
        student.setAge(5);
        insert(student);
        System.out.println("规则rule_student_age等于10岁触发");
end

rule "rule_student_age小于10岁"
    when
        $s:Student(age < 10)
    then
        $s.setAge(15);
        update($s);
        System.out.println("规则rule_student_age小于10岁触发");
end

rule "rule_student_age小于20岁同时大于10岁"
    when
        $s:Student(age < 20 && age > 10)
    then
        $s.setAge(25);
        update($s);
        System.out.println("规则rule_student_age小于20岁同时大于10岁触发");
end

rule "rule_student_age大于20岁"
    when
        $s:Student(age > 20)
    then
        System.out.println("规则rule_student_age大于20岁触发");
end

第二步:编写单元测试

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

Student student = new Student();
student.setAge(10);

//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
kieSession.insert(student);

kieSession.fireAllRules();
kieSession.dispose();

通过控制台输出可以发现,只有第一个规则触发了,因为在第一个规则中将工作内存中的数据删除了导致第二个规则并没有匹配成功。

4. 规则属性

前面我们已经知道了规则体的构成如下:

代码语言:javascript复制
rule "ruleName"
    attributes
    when
        LHS
    then
        RHS
end

本章节就是针对规则体的attributes属性部分进行讲解。Drools中提供的属性如下表(部分属性):

属性名

说明

salience

指定规则执行优先级

dialect

指定规则使用的语言类型,取值为java和mvel

enabled

指定规则是否启用

date-effective

指定规则生效时间

date-expires

指定规则失效时间

activation-group

激活分组,具有相同分组名称的规则只能有一个规则触发

agenda-group

议程分组,只有获取焦点的组中的规则才有可能触发

timer

定时器,指定规则触发的时间

auto-focus

自动获取焦点,一般结合agenda-group一起使用

no-loop

防止死循环

4.1 enabled属性

enabled属性对应的取值为true和false,默认值为true。

用于指定当前规则是否启用,如果设置的值为false则当前规则无论是否匹配成功都不会触发

代码语言:javascript复制
rule "rule_comparison_notMemberOf"
    //指定当前规则不可用,当前规则无论是否匹配成功都不会执行
    enabled false
    when
        ComparisonOperatorEntity(names not memberOf list)
    then
        System.out.println("规则rule_comparison_notMemberOf触发");
end

4.2 dialect属性

dialect属性用于指定当前规则使用的语言类型,取值为java和mvel,默认值为java。

注:mvel是一种基于java语法的表达式语言。

mvel像正则表达式一样,有直接支持集合、数组和字符串匹配的操作符。

mvel还提供了用来配置和构造字符串的模板语言。

mvel表达式内容包括属性表达式,布尔表达式,方法调用,变量赋值,函数定义等。

4.3 salience属性

salience属性用于指定规则的执行优先级,取值类型为Integer数值越大越优先执行。每个规则都有一个默认的执行顺序,如果不设置salience属性,规则体的执行顺序为由上到下。

可以通过创建规则文件salience.drl来测试salience属性,内容如下:

代码语言:javascript复制
package test.salience

rule "rule_1"
    when
        eval(true)
    then
        System.out.println("规则rule_1触发");
end
    
rule "rule_2"
    when
        eval(true)
    then
        System.out.println("规则rule_2触发");
end

rule "rule_3"
    when
        eval(true)
    then
        System.out.println("规则rule_3触发");
end

通过控制台可以看到,由于以上三个规则没有设置salience属性,所以执行的顺序是按照规则文件中规则的顺序由上到下执行的。接下来我们修改一下文件内容:

代码语言:javascript复制
package testsalience

rule "rule_1"
    salience 9
    when
        eval(true)
    then
        System.out.println("规则rule_1触发");
end

rule "rule_2"
    salience 10
    when
        eval(true)
    then
        System.out.println("规则rule_2触发");
end

rule "rule_3"
    salience 8
    when
        eval(true)
    then
        System.out.println("规则rule_3触发");
end

通过控制台可以看到,规则文件执行的顺序是按照我们设置的salience值由大到小顺序执行的。

建议在编写规则时使用salience属性明确指定执行优先级。

4.4 no-loop属性

no-loop属性用于防止死循环,当规则通过update之类的函数修改了Fact对象时,可能使当前规则再次被激活从而导致死循环。取值类型为Boolean,默认值为false。测试步骤如下:

第一步:编写规则文件/resource/rules/noloop.drl

代码语言:javascript复制
package testnoloop
import com.itheima.drools.entity.Student
/*
    此规则文件用于测试no-loop属性
*/
rule "rule_noloop"
    when
        // no-loop true
        $student:Student(age == 25)
    then
        update($student);//注意此处执行update会导致当前规则重新被激活
        System.out.println("规则rule_noloop触发");
end

第二步:编写单元测试

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

Student student = new Student();
student.setAge(25);

//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配,如果规则匹配成功则执行规则
kieSession.insert(student);

kieSession.fireAllRules();
kieSession.dispose();

通过控制台可以看到,由于我们没有设置no-loop属性的值,所以发生了死循环。接下来设置no-loop的值为true再次测试则不会发生死循环。

4.5 activation-group属性

activation-group属性是指激活分组,取值为String类型。具有相同分组名称的规则只能有一个规则被触发。

第一步:编写规则文件/resources/rules/activationgroup.drl

代码语言:javascript复制
package testactivationgroup
/*
    此规则文件用于测试activation-group属性
*/
    
rule "rule_activationgroup_1"
    activation-group "mygroup"
    when
    then
        System.out.println("规则rule_activationgroup_1触发");
end

rule "rule_activationgroup_2"
    activation-group "mygroup"
    when
    then
        System.out.println("规则rule_activationgroup_2触发");
end

第二步:编写单元测试

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

通过控制台可以发现,上面的两个规则因为属于同一个分组,所以只有一个触发了。同一个分组中的多个规则如果都能够匹配成功,具体哪一个最终能够被触发可以通过salience属性确定。

4.6 agenda-group属性

agenda-group属性为议程分组,属于另一种可控的规则执行方式。用户可以通过设置agenda-group来控制规则的执行,只有获取焦点的组中的规则才会被触发。

第一步:创建规则文件/resources/rules/agendagroup.drl

代码语言:javascript复制
package testagendagroup
/*
    此规则文件用于测试agenda-group属性
*/
rule "rule_agendagroup_1"
    agenda-group "myagendagroup_1"
    when
    then
        System.out.println("规则rule_agendagroup_1触发");
end

rule "rule_agendagroup_2"
    agenda-group "myagendagroup_1"
    when
    then
        System.out.println("规则rule_agendagroup_2触发");
end
//========================================================
rule "rule_agendagroup_3"
    agenda-group "myagendagroup_2"
    when
    then
        System.out.println("规则rule_agendagroup_3触发");
end

rule "rule_agendagroup_4"
    agenda-group "myagendagroup_2"
    when
    then
        System.out.println("规则rule_agendagroup_4触发");
end

第二步:编写单元测试

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

//设置焦点,对应agenda-group分组中的规则才可能被触发
kieSession.getAgenda().getAgendaGroup("myagendagroup_1").setFocus();

kieSession.fireAllRules();
kieSession.dispose();

通过控制台可以看到,只有获取焦点的分组中的规则才会触发。与activation-group不同的是,activation-group定义的分组中只能够有一个规则可以被触发,而agenda-group分组中的多个规则都可以被触发。

4.7 auto-focus属性

auto-focus属性为自动获取焦点,取值类型为Boolean,默认值为false。一般结合agenda-group属性使用,当一个议程分组未获取焦点时,可以设置auto-focus属性来控制。

第一步:修改/resources/rules/agendagroup.drl文件内容如下

代码语言:javascript复制
package testagendagroup

rule "rule_agendagroup_1"
    agenda-group "myagendagroup_1"
    when
    then
        System.out.println("规则rule_agendagroup_1触发");
end

rule "rule_agendagroup_2"
    agenda-group "myagendagroup_1"
    when
    then
        System.out.println("规则rule_agendagroup_2触发");
end
//========================================================
rule "rule_agendagroup_3"
    agenda-group "myagendagroup_2"
    auto-focus true //自动获取焦点
    when
    then
        System.out.println("规则rule_agendagroup_3触发");
end

rule "rule_agendagroup_4"
    agenda-group "myagendagroup_2"
    auto-focus true //自动获取焦点
    when
    then
        System.out.println("规则rule_agendagroup_4触发");
end

第二步:编写单元测试

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

通过控制台可以看到,设置auto-focus属性为true的规则都触发了。

注意:同一个组,只要有个设置auto-focus true 其他的设置不设置都无所谓啦。都会起作用的。

4.8 timer属性

timer属性可以通过定时器的方式指定规则执行的时间,使用方式有两种:

方式一:timer (int: ?)

此种方式遵循java.util.Timer对象的使用方式,第一个参数表示几秒后执行,第二个参数表示每隔几秒执行一次,第二个参数为可选。

方式二:timer(cron: )

此种方式使用标准的unix cron表达式的使用方式来定义规则执行的时间。

第一步:创建规则文件/resources/rules/timer.drl

代码语言:javascript复制
package testtimer
import java.text.SimpleDateFormat
import java.util.Date
/*
    此规则文件用于测试timer属性
*/

rule "rule_timer_1"
    timer (5s 2s) //含义:5秒后触发,然后每隔2秒触发一次
    when
    then
        System.out.println("规则rule_timer_1触发,触发时间为:"   
                         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
end

rule "rule_timer_2"
    timer (cron:0/1 * * * * ?) //含义:每隔1秒触发一次
    when
    then
        System.out.println("规则rule_timer_2触发,触发时间为:"   
                         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
end

第二步:编写单元测试

代码语言:javascript复制
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
final KieSession kieSession = kieClasspathContainer.newKieSession();

new Thread(new Runnable() {
    public void run() {
        //启动规则引擎进行规则匹配,直到调用halt方法才结束规则引擎
        kieSession.fireUntilHalt();
    }
}).start();

Thread.sleep(10000);
//结束规则引擎
kieSession.halt();
kieSession.dispose();

注意:单元测试的代码和以前的有所不同,因为我们规则文件中使用到了timer进行定时执行,需要程序能够持续一段时间才能够看到定时器触发的效果。

4.9 date-effective属性

date-effective属性用于指定规则的生效时间,即只有当前系统时间大于等于设置的时间或者日期规则才有可能触发。默认日期格式为:dd-MMM-yyyy。用户也可以自定义日期格式。

第一步:编写规则文件/resources/rules/dateeffective.drl

代码语言:javascript复制
package testdateeffective
/*
    此规则文件用于测试date-effective属性
*/
rule "rule_dateeffective_1"
    date-effective "2020-10-01 10:00"
    when
    then
        System.out.println("规则rule_dateeffective_1触发");
end

第二步:编写单元测试

代码语言:javascript复制
//设置日期格式
System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

注意:上面的代码需要设置日期格式,否则我们在规则文件中写的日期格式和默认的日期格式不匹配程序会报错。

4.10 date-expires属性

date-expires属性用于指定规则的失效时间,即只有当前系统时间小于设置的时间或者日期规则才有可能触发。默认日期格式为:dd-MMM-yyyy。用户也可以自定义日期格式。

第一步:编写规则文件/resource/rules/dateexpires.drl

代码语言:javascript复制
package testdateexpires
/*
    此规则文件用于测试date-expires属性
*/

rule "rule_dateexpires_1"
    date-expires "2019-10-01 10:00"
    when
    then
        System.out.println("规则rule_dateexpires_1触发");
end

第二步:编写单元测试

代码语言:javascript复制
//设置日期格式
System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

注意:上面的代码需要设置日期格式,否则我们在规则文件中写的日期格式和默认的日期格式不匹配程序会报错。

参考资料

1.Drools5规则引擎开发教程

0 人点赞