目录
  • 一、背景
    • 1.Drools介绍
  • 二、为什么要学习drools
    • 三、实现上方这个简单的打折案例
      • 1、引入jar包
      • 2、编写kmodule.xml配置文件
      • 3、编写规则文件
        • 1、规则文件的语法
        • 2、编写规则文件
        • 3、解释一下包名
    • 四、编写Java代码
      • 1、编写一个订单对象
        • 2、编写测试代码
        • 五、测试结果
          • 六、drools引擎的基本组件
            • 七、完整代码
              • 八、参考文档

                一、背景

                最近在学习规则引擎drools,此处简单记录一下drools的入门案例。

                1.Drools介绍

                drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。

                drools官网地址:https://drools.org/

                drools源码下载地址:https://github.com/kiegroup/drools
                ———————————–
                Drools:概述和入门案例:https://www.jb51.net/article/248263.htm

                二、为什么要学习drools

                假设我们存在如下场景:
                在我们到商店购买衣服的时候,经常会发生这样的事情,购买1件不打折,购买2件打0.98折,购买3件级以上打0.85折。
                那么我们在代码中如果要实现上述功能,是不是就需要编写if ... else语句,假设后期规则变了,是不是就需要修改这些if ... else语句,然后程序重新部署。这样是可以实现,但是不够优雅。那么我们是否可以将这些业务规则写入到规则文件中,以后规则变更直接修改规则文件即可?而drools就可以实现这个功能。

                三、实现上方这个简单的打折案例

                1、引入jar包

                <dependencyManagement>
                    <dependencies>
                        <dependency>
                            <groupId>org.drools</groupId>
                            <artifactId>drools-bom</artifactId>
                            <type>pom</type>
                            <version>7.69.0.Final</version>
                            <scope>import</scope>
                        </dependency>
                    </dependencies>
                </dependencyManagement>
                
                <dependencies>
                    <dependency>
                       <groupId>org.drools</groupId>
                        <artifactId>drools-compiler</artifactId>
                    </dependency>
                    <dependency>
                        <groupId>org.drools</groupId>
                        <artifactId>drools-mvel</artifactId>
                    </dependency>
                    <dependency>
                        <groupId>ch.qos.logback</groupId>
                        <artifactId>logback-classic</artifactId>
                        <version>1.2.11</version>
                    </dependency>
                </dependencies>

                2、编写kmodule.xml配置文件

                此配置文件需要放置在resources/META-INF目录下。

                <kmodule xmlns="http://www.drools.org/xsd/kmodule">
                    <!--
                        kbase 可以存在多个
                        name: 指定kbase的名字,需要是唯一的
                        packages: 包名,可以理解为到src/main/resources目录下查找这个包名下的规则文件,多个包使用逗号分割
                        default: 当前kbase是否是默认的kbase
                    -->
                    <kbase name="shop-kabse" packages="com.huan.shop" default="false">
                        <!--
                            ksession 可以存在多个
                            name: 指定ksession 的名字,需要唯一
                            defalut: 当前ksession在这个kbase下是否是默认的
                            type: 指定当前ksession是否是有状态的 stateless表示是无状态的
                        -->
                        <ksession name="shop-ksession" default="false" type="stateless"/>
                        <ksession name="shop-ksession-stateful" default="false" type="stateful"/>
                    </kbase>
                </kmodule>

                此处我们需要关注一下 kbasepackage的值,这个值需要和规则文件中的package值一致,否则会找不到规则,具体看下方。

                3、编写规则文件

                1、规则文件的语法

                包名,必须放置在第一行
                package
                // 引入Java中的类,需要些全限定名
                import
                // 定义function ,可选
                function  // Optional
                // 定义 query ,可选
                query  // Optional
                declare   // Optional
                global   // Optional
                // rule 关键字 "rule name" 规则的名字
                rule "rule name"
                    // Attributes 属性可选
                    when  // 关键字
                        // Conditions  条件,可为空
                    then
                        // Actions // 匹配后执行的结果
                end // 关键字

                2、编写规则文件

                规则文件的名字无所谓,比如: book-discount.drl

                // 包名,必须防止到第一行,这个名字需要和 kbase中package属性的值一致
                package com.huan.shop
                /**
                 * 倒入类
                 */
                import com.huan.drools.CustomerOrder
                // 定义规则
                rule "shop-rule-01"
                    when
                        // 模式匹配:到工作内存中查找CustomerOrder,并且这个对象的purchaseQuantity值需要是1,
                        // 如果条件成立,$order是绑定变量名,一般以$开头,和fact对象区分开
                        $order:CustomerOrder(purchaseQuantity == 1)
                    then
                        System.out.println("匹配规则 shop-rule-01");
                        // 赋值,此处赋值后,在Java代码中获取获取到赋值后的值
                        $order.setDiscount(1D);
                end
                rule "shop-rule-02"
                    when
                        $order:CustomerOrder(purchaseQuantity == 2)
                    then
                        System.out.println("匹配规则 shop-rule-02");
                        $order.setDiscount(0.98);
                end
                rule "shop-rule-03"
                    when
                        $order:CustomerOrder(purchaseQuantity >= 3)
                    then
                        System.out.println("匹配规则 shop-rule-03");
                        $order.setDiscount(0.85);
                end

                3、解释一下包名

                drools的简单入门案例场景分析

                如果 shop-discount.drl的包名修改为com.huan.shop1则会提示如下警告:

                12:43:01.589 [main] WARN org.drools.compiler.kie.builder.impl.KieBuilderImpl – File 'com/huan/shop/shop-discount.drl' is in folder 'com/huan/shop' but declares package 'com.huan.shop1'. It is advised to have a correspondance between package and folder names.

                四、编写Java代码

                1、编写一个订单对象

                此对象保存的是用户购买了几件衣服和对应的折扣。

                /**
                 * 客户购买衣服的订单,省略 getter 和 setter 方法
                 *
                 * @author huan.fu
                 * @date 2022/5/12 - 11:27
                 */
                public class CustomerOrder {
                    /**
                     * 购买了几件衣服
                     */
                    private Integer purchaseQuantity;
                    /**
                     * 最终打多少折
                     */
                    private Double discount;
                    public CustomerOrder(Integer purchaseQuantity) {
                        this.purchaseQuantity = purchaseQuantity;
                    }
                }

                2、编写测试代码

                1、无状态测试方法statelessSessionTest规则规则2,即最终打0.98折。
                2、有状态测试方法statefulSessionTest规则规则3,即最终打0.85折。

                package com.huan.drools;
                import org.kie.api.KieServices;
                import org.kie.api.event.rule.DebugRuleRuntimeEventListener;
                import org.kie.api.runtime.KieContainer;
                import org.kie.api.runtime.KieSession;
                import org.kie.api.runtime.StatelessKieSession;
                /**
                 * drools 测试类
                 */
                public class DroolsApplication {
                    public static void main(String[] args) throws InterruptedException {
                        // 无状态session测试
                        statelessSessionTest();
                        // 有状态session测试
                        statefulSessionTest();
                    }
                    private static void statelessSessionTest() {
                        // 获取kie services
                        KieServices kieServices = KieServices.get();
                        // 获取kie容器对象
                        KieContainer kieContainer = kieServices.getKieClasspathContainer();
                        // 获取kie session , 此处获取的是无状态的session,因为 <ksession name="shop-ksession" default="false" type="stateless"/>
                        // 中type="stateless"就是无状态的session
                        StatelessKieSession kieSession = kieContainer.newStatelessKieSession("shop-ksession");
                        // 创建一个对象,可以理解为 Fact对象,即事实对象
                        CustomerOrder customerOrder = new CustomerOrder(2);
                        // 添加监听器,便于观察日志
                        kieSession.addEventListener(new DebugRuleRuntimeEventListener());
                        // 无状态的session只需要执行 execute 方法即可。
                        kieSession.execute(customerOrder);
                        System.err.println("通过规则后,获取到的折扣为:" + customerOrder.getDiscount());
                    }
                    private static void statefulSessionTest() {
                        // 获取kie services
                        KieServices kieServices = KieServices.get();
                        // 获取kie容器对象
                        KieContainer kieContainer = kieServices.getKieClasspathContainer();
                        // 获取kie session , 此处获取的是有状态的session
                        KieSession kieSession = kieContainer.newKieSession("shop-ksession-stateful");
                        // 创建一个对象,可以理解为 Fact对象,即事实对象
                        CustomerOrder customerOrder = new CustomerOrder(3);
                        // 添加监听器,便于观察日志
                        kieSession.addEventListener(new DebugRuleRuntimeEventListener());
                        // 将customerOrder对象加入到工作内存中
                        kieSession.insert(customerOrder);
                        // 触发所有的规则,如果只想触发指定的规则,则使用fireAllRules(AgendaFilter agendaFilter)方法
                        kieSession.fireAllRules();
                        // 有状态的session一定需要调用dispose方法
                        kieSession.dispose();
                        System.err.println("通过规则后,获取到的折扣为:" + customerOrder.getDiscount());
                    }
                }
                

                此处需要注意有状态session无状态session写法的区别。

                五、测试结果

                drools的简单入门案例场景分析

                到此,我们使用drools实现的一个简单的案例就实现了。

                六、drools引擎的基本组件

                drools的简单入门案例场景分析

                1、Rules:我们自己定义的业务规则,比如我们自己写的规则文件。所有规则必须至少包含触发规则的条件和规则规定的操作。
                2、Production memory:规则存储在 Drools 引擎中的位置。
                3、Facts:输入或更改到 Drools 引擎中的数据,Drools 引擎匹配规则条件以执行适用规则。在规则中修改了Fact对象的值,真实的JavaBean的数据也会发生改变。
                比如:当我们调用ksession.insert(对象),那么插入的这个对象就可以理解成Facts对象。
                4、Working memory:facts 在 Drools 引擎中存储的位置。
                5、Pattern matcher:匹配器,将Rule Base中所有的规则与Working memory中的Fact对象进行模式匹配,匹配成功的规则将被激活并放入到Agenda中。
                6、Agenda:议程,执行Agenda中被激活的排好序的规则。

                七、完整代码

                https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-quickstart

                八、参考文档

                1、https://docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html#decision-engine-con_decision-engine

                声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。