规则引擎Drools学习日志

本篇博客纯属学习记录日志,详情请参考视频

规则引擎

概述: 将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理。

产品:drools、VisualRules、iLog等。

运行: 接收数据输入,解释业务规则,并根据业务规则做出业务决策。输入输出平台

Drools

drools API 开发步骤

1. 获取KieServices  
2. 获取KieContainer 
3. KieSession 
4. Insert fact 
5. 触发规则
6. 关闭KieSession

入门实例 Maven 项目

  1. 创建Maven项目

  2. 引入配置 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>
    
  3. 建立实体类

    package com.item.drools.entity
        
    public Class Order{
        private float originalPrice;
        private float realPrice;
    }
    
  4. 创建规则文件 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
    
    
  5. 编写单元测试

    @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中被激活的规则

规则引擎执行过程

  1. 将初始数据(fact) 输入至工作内存 (Working Memory)
  2. 使用Pattern Matcher 将规则库中的规则 (rule) 和数据 (fact) 比较
  3. 如果执行规则存在冲突 (conflict) ,即同时激活了多个规则,将冲突的规则放入冲突集合
  4. 解决冲突,将激活的规则按顺序放入Agenda
  5. 执行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

通过上面的例子,匹配的条件为:

  1. 工作内存中必须存在Order这种类型的Fact对象 —— 类型约束
  2. Fact对象的originalPrice属性值必须小于200 ——属性约束
  3. 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定义的全局变量时需要注意两点:

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

  1. 创建工程,并配置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>
    
  2. 创建/resources/application.yml文件或者修改/resources/application.approperties文件

    server:
    	port: 8080
    spring:
    	application:
    		name: drools_springboot
    
  3. 创建规则文件/resources/rules/helloworld.drl

    package helloworld
    
    rule "hello_world"
        when
        	eval(true)
        then
        	System.out.println("规则hello_world触发")
    end
    
  4. 编写配置类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"));
            }
        }
    }
    
  5. 创建RuleServices类

  6. 创建Controller

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值