Drools规则引擎

Drools规则引擎

1、Drools简介

Drools(Drools Rule Engine)是一个开源的规则引擎,它主要用于在Java应用程序中实现规则管理。Drools规则引擎将规则定义和管理从应用程序代码中分离出来,使得规则可以独立于应用程序运行。这样可以提高规则的可靠性和可维护性,同时也可以使得规则的更新和管理更加方便。

Drools规则引擎的主要特点包括:

  1. 将规则定义和管理从应用程序代码中分离出来,使得规则可以独立于应用程序运行。
  2. 提供基于规则的访问和操作数据的功能,例如过滤、排序、检索等。
  3. 支持动态规则扩展和维护,可以根据需要添加、删除或修改规则。
  4. 规则引擎是相对独立的,只关心业务规则,使得业务分析人员也可以参与编辑、维护系统的业务规则。
  5. 减少了硬编码业务规则的成本和风险。
  6. 使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单。

Drools规则引擎在企业应用中被广泛应用,例如在电子商务、社交网络、移动应用等领域中,都可以使用Drools规则引擎来实现规则管理。

drools官网:[Drools - Drools - Business Rules Management System (Java™, Open Source)](https://www.drools.org/)

drools源码:GitHub - kiegroup/drools: Drools is a rule engine, DMN engine and complex event processing (CEP) engine for Java.

drools API开发步骤:获取KieServices => 获取KieContainer => kieSession => Insert fact => 触发规则 => 关闭KieSession

核心类关系

请添加图片描述

Kie全称为Knowledge ls Everything,即"知识就是一切"的缩写,是Jboss一系列项目的总称

规则引擎构成
drools规则引擎由以下三部分构成:

  • Working Memory(工作内存)
  • Rule Base (规则库)
  • Inference Engine (推理引擎)

其中 Inference Engine (推理引擎)又包括:

  • Pattern Matcher(匹配器)具体匹配哪一个规则,由这个完成
  • Agenda(议程)
  • Executior Engine (执行引擎)

2、Drools入门案例

2.1、业务场景

用户购买的金额和对应送多少积分的规则如下:

100元以下,不加分
100元-500元加100分
500元-1000元加500分
1000元以上加1000分

2.2、maven坐标

注意:不同版本可能有所不同,有些模块被单独拆分出来成为一个单独的模块,例如 drools-mvel 被从core单独拆分出来。

<!--drools规则引擎-->
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.73.0.Final</version>
</dependency>

2.3、编写xml配置文件(多方法)

根据drools要求创建resources/META-INF/kmodule.xml配置文件
需要有一个配置文件告诉代码规则文件drl在哪里,在drools中这个文件就是kmodule.xml,放置到resources/META-INF目录下。

<?xml version="1.0" encoding="utf-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <!--
        name: kbase的名称
        packages: kbase下的package
        default: 默认的kbase
    -->
    <kbase name="rules" packages="droolsRule" default="true">
        <!--
            name: ksession的名称,需要唯一
            type: ksession的类型
            default: 默认的ksession
        -->
        <ksession name="rulesSession" default="true"/>
    </kbase>
</kmodule>

注意:上面配置文件的名称和位置都是固定写法,不能更改Kmodule 中可以包含一个到多个kbase,分别对应drl的规则文件。
Kbase 需要一个唯一的name,可以取任意字符串。
packages 为drl文件所在resource目录下的路径。注意区分drl文件中的package与此处的package不一定相同。多个包用逗号分隔。默认情况下会扫描resources目录下所有(包含子目录)规则文件。
kbase的default属性标示当前KieBase是不是默认的,如果是默认的则不用名称就可以查找到该KieBase,但每个module最多只能有一个默认KieBase。
kbase下面可以有一个或多个ksession, ksession的name 属性必须设置,且必须唯一。

2.4、创建drl规则文件

创建规则文件resources/droolsRule/rule1.drl

package rules;

import com.hippo.drools.entity.Order;


//100元以下,不加分
rule "score_1"
when
    $order:Order(price<100)
then
    $order.setScore($order.getScore()+0);
    System.out.println("触发了规则:100元以下,不加分");
end
//100元-500元加100分
rule "score_2"
when
    $order:Order(price>=100&&price<500)
then
    $order.setScore($order.getScore()+100);
    System.out.println("触发了规则:100元-500元加100分");
end
//500元-1000元加500分
rule "score_3"
when
    $order:Order(price>=500&&price<1000)
then
    $order.setScore($order.getScore()+500);
    System.out.println("触发了规则:500元-1000元加500分");
end
//1000元以上加1000分
rule "score_4"
when
    $order:Order(price>=1000)
then
    $order.setScore($order.getScore()+1000);
    System.out.println("触发了规则:1000元以上加1000分");
end

2.5、单元测试

创建Order实体

@Data
@AllArgsConstructor
@NoArgsConstructor
@Schema(name = "订单类")
public class Order {
   
   
    @Schema(name = "价格", example = "1")
    private Integer price;
    @Schema(name = "积分", example = "1")
    private Integer score;
}

创建测试类型

package com.hippo.provider;

import com.hippo.drools.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.boot.test.context.SpringBootTest;

import java.math.BigDecimal;

/**
 * @ClassName DroolsTest
 * @Description TODO drools规则引擎单元测试
 * @Author tangxl
 * @create 2023-05-09 18:59
 **/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class DroolsTest {
   
   
    @Test
    public void test(){
   
   
        // 第一步:初始化一个KieServices
        KieServices kieServices = KieServices.Factory.get();
        // 第二步:获取KieContainer会话对象
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        // 第三步:初始化KieSession
        KieSession kieSession = kieContainer.newKieSession();
        // 构造订单对象,设置订单价格,由规则引擎来计算订单积分
        Order order = new Order(500, 1);
        log.info("执行规则前的订单信息:{}",order);
        //第四步:将数据对象交给规则引擎,规则引擎根据提供的数据进行规则匹配
        kieSession.insert(order);

        // 第五步:激活规则引擎,如果符合规则执行规则
        kieSession.fireAllRules();
        // 第六步:关闭会话
        kieSession.dispose();
        // 结果打印
        log.info("执行规则后的订单信息:{}",order);
    }
}

执行输出结果

2023-05-10T09:57:16.723+08:00  INFO 13364 --- [           main] o.d.c.kie.builder.impl.KieContainerImpl  : Start creation of KieBase: myKbase1
2023-05-10T09:57:17.746+08:00  INFO 13364 --- [           main] o.d.c.kie.builder.impl.KieContainerImpl  : End creation of KieBase: myKbase1
2023-05-10T09:57:17.812+08:00  INFO 13364 --- [           main] com.hippo.provider.DroolsTest            : 执行规则前的订单信息:Order(price=500, score=1)
触发了规则:500元-1000元加500分
2023-05-10T09:57:17.864+08:00  INFO 13364 --- [           main] com.hippo.provider.DroolsTest            : 执行规则后的订单信息:Order(price=500, score=501)

3、Drools基础语法

3.1、规则文件的构成

drl是Drools Rule Language的缩写。

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

  • package:包名,package对应的不一定是真正的目录,可以任意写com.rules,同一个包下的drl文件可以相互访问.
  • import:用于导入类或者静态方法
  • global:全局变量
  • function:自定义函数
  • query:查询
  • rule end:规则体

3.2、规则体语法结构

一个规则通常包括三个部分:属性部分(attribute)、条件部分(LHS)和结果部分(RHS)

rule "ruleName"		// rule关键字,表示规则开始,参数为规则的唯一名称
	attribute		// 规则属性,是rule与when之间的参数,为可选项
	when			// 关键字,后面是规则的条件部分
		LHS			// left Hand Side,是规则的条件部分
	then			// 后面跟规则的结果或行为
		RHS			// 是规则的结果或行为
end 				// 表示一个规则的结束

3.2.1、条件部分

LHS(Left Hand Side):是规则的条件部分的通用名称。它由零个或多个条件元素组成。如果LHS为空,则它将被视为始终为true的条件元素。

LHS部分由一个或多个条件组成,条件又称为pattern

pattern的语法结构:绑定变量名:Object(Filed约束)

其中绑定变量名可以省略,通常绑定变量名的命名一般建议以**$开始**。如果定义了绑定变量名,就可以在规则体的RHS部分使用此绑定变量名来操作相应的Fact对象。Field约束部分是需要返回true或者false的0个或多个表达式。

//规则一:100元以下,不加分
rule "score_1"
when
	// 工作内存中必须存在Order这种类型的Fact对象----类型约束
    // Fact对象的price属性必须小于100----属性约束
    $order:Order(price<100)
then
    $order.setScore($order.getScore()+0);
    System.out.println("触发了规则:100元以下,不加分");
end

注:如果 LHS 为空,那么引擎会自动添加一个 eval(true) 的条件,由于该条件总是返回 true,所以 LHS 为空的规则总是返回 true.

3.2.1.1、约束连接

在 LHS 当中,可以包含 0~n 个条件,多个 pattern 之间可以采用 “&&”(and) 、 “lI”(or) 和 “,”(and) 来实现,也可以不写,默认连接为and。

//规则二:100元-500元加100分
rule "score_2"
when
    $order:Order(price>=100 && price<500)
then
    $order.setScore($order.getScore()+100);
    System.out.println("触发了规则:100元-500元加100分");
end
3.2.1.2、比较操作符

在Drools当中共提供了十二种类型的比较操作符,分别是: >、>=、<、<=、==、!=、contains、not contains、 memberof、not memberof、matches、not matches;在这十二种类型的比较操作符当中,前六个是比较常见也是用的比较多的比较操作符

符号 说明
> 大于
< 小于
>= 大于等于
<= 小于等于
== 等于
!= 不等于
contains 检查一个Fact对象的某个属性是否包含一个指定的对象值
not contains 检查一个Fact对象的某个属性是否包含一个指定的对象值
memberOf 判断一个Fact对象的某个属性是否在一个或多个集合中
not memberOf 判断一个Fact对象的某个属性是否在一个或多个集合中
matches 判断一个Fact对象的属性是否与提供的标准Java正则表达式进行匹配
not matches 判断一个Fact对象的属性是否与提供的标准Java正则表达式进行匹配

案例:校验container是否包含order订单对象

第一步:创建Customer对象

package com.hippo.drools.fact;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.util.List;

/**
 * @ClassName Customer
 * @Description TODO 客户对象Fact
 * @Author tangxl
 * @create 2023-05-10 10:55
 **/
@Data
@Schema(name = "客户对象Fact")
public class Customer {
   
   
    @Schema(name = "客户ID", example = "1")
    private Integer id;

    @Schema(name = "客户名称", example = "张三")
    private String name;

    @Schema(name = "客户订单", example = "")
    private List<Order> orders;
}

第二步:新建drl文件

package rules.temprules;

import java.util.List;
import com.hippo.drools.fact.*;

rule "temprule11"
    when
        $order : Order()
        $customer : Customer( orders contains $order )
    then
        System.out.println( "触发规则:客户对象里面包含订单" );
    end

第三步:新建测试单元

    @Test
    public void test2(){
   
   
        KieServices kieServices = KieServices.Factory.get();
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        KieSession kieSession = kieContainer.newKieSession();

        Order order = new Order(500, 1);
        Customer customer = new Customer();
        customer.setName("张三");
        customer.setOrders(Arrays.asList(order));

        kieSession.insert(order);
        kieSession.insert(customer);
        // RuleNameEndsWithAgendaFilter 用于过滤规则名称为temprule11的规则
//        kieSession.fireAllRules();
        kieSession.fireAllRules(new RuleNameEndsWithAgendaFilter("temprule11"));
        kieSession.dispose();
    }

结果一:未添加过滤器

16:42:31.425 [main] INFO org.drools.compiler.kie.builder.impl.InternalKieModuleProvider - Creating KieModule for artifact com.hippo:drools:0.0.1-SNAPSHOT
16:42:31.429 [main] DEBUG org.drools.compiler.kie.builder.impl.ClasspathKieProject - Discovered classpath module com.hippo:drools:0.0.1-SNAPSHOT
16:42:31.436 [main] DEBUG org.drools.compiler.kie.builder.impl.KieRepositoryImpl - KieModule was added: FileKieModule[releaseId=com.hippo:drools:0.0.1-SNAPSHOT,file=D:\Tools\SrcProject\learning\java-study\study-commons\drools\target\classes]
16:42:31.440 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - Start creation of KieBase: myKbase1
16:42:31.488 [main] DEBUG org.drools.compiler.compiler.JavaDialectConfiguration - Selected compiler ECLIPSE [drools.dialect.java.compiler:null, hasEclipseCompiler:true]
16:42:31.606 [main] DEBUG org.drools.compiler.compiler.JavaDialectConfiguration - Selected compiler ECLIPSE [drools.dialect.java.compiler:null, hasEclipseCompiler:true]
16:42:31.606 [main] DEBUG org.drools.compiler.compiler.JavaDialectConfiguration - Selected compiler ECLIPSE [drools.dialect.java.compiler:null, hasEclipseCompiler:true]
16:42:32.969 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - End creation of KieBase: myKbase1
16:42:33.038 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
触发规则:客户对象里面包含订单
触发了规则:500元-1000元加500分
16:42:33.093 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
16:42:33.093 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is now INACTIVE
16:42:33.093 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now DISPOSED

结果二:添加过滤器 RuleNameEndsWithAgendaFilter 对规则名称进行过滤

16:39:53.563 [main] INFO org.drools.compiler.kie.builder.impl.InternalKieModuleProvider - Creating KieModule for artifact com.hippo:drools:0.0.1-SNAPSHOT
16:39:53.567 [main] DEBUG org.drools.compiler.kie.builder.impl.ClasspathKieProject - Discovered classpath module com.hippo:drools:0.0.1-SNAPSHOT
16:39:53.573 [main] DEBUG org.drools.compiler.kie.builder.impl.KieRepositoryImpl - KieModule was added: FileKieModule[releaseId=com.hippo:drools:0.0.1-SNAPSHOT,file=D:\Tools\SrcProject\learning\java-study\study-commons\drools\target\classes]
16:39:53.579 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - Start creation of KieBase: myKbase1
16:39:53.620 [main] DEBUG org.drools.compiler.compiler.JavaDialectConfiguration - Selected compiler ECLIPSE [drools.dialect.java.compiler:null, hasEclipseCompiler:true]
16:39:53.692 [main] DEBUG org.drools.compiler.compiler.JavaDialectConfiguration - Selected compiler ECLIPSE [drools.dialect.java.compiler:null, hasEclipseCompiler:true]
16:39:53.692 [main] DEBUG org.drools.compiler.compiler.JavaDialectConfiguration - Selected compiler ECLIPSE [drools.dialect.java.compiler:null, hasEclipseCompiler:true]
16:39:54.619 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - End creation of KieBase: myKbase1
16:39:54.676 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
触发规则:客户对象里面包含订单
16:39:54.720 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
16:39:54.720 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is now INACTIVE
16:39:54.720 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now DISPOSED

注意:使用 contains 时,需要传入此 Fact 对象属性值里面需要校验 是否包含的对象 ,这里可以使用 RuleNameEndsWithAgendaFilter 等过滤器对规则名称进行限制,不让传入 校验是否包含的对象 也进行一次规则校验。

3.2.2、结果部分

在Drools当中,在RHS里面,提供了一些对当前Working Memory 实现快速操作的宏宏函数或对象,比如 insert/insertLogical、update 和 retract 就可以实现对当前 Working Memory 中的 Fact 对象进行新增、删除或者是修改。

3.2.2.1、insert

函数 insert 的作用与我们在Java类当中调用 StatefulKnowledgeSession 对象的 insert 方法的作用相同,都是用来将一个 Fact 对象插入到当前的 Working Memory 当中
注意:一旦调用 insert 宏函数,那么 Drools 会重新与所有的规则再重新匹配一次,对于没有设置 no-loop 属性为 true 的规则,如果条件满足,不管其之前是否执行过都会再执行一次,这个特性不仅存在于 insert 宏函数上,后面介绍的 update、retract 宏函数同样具有该特性,所以在某些情况下因考虑不周调用 insert、update 或 retract 容易发生死循环

示例:

添加规则

rule "temprule2"
    when
     eval( true ) // 为了测试,这里写死了都需要执行
    then
        Customer customer = new Customer();
        customer.setName</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hippoDocker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值