一、背景

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

二、为什么要学习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  // Optionaldeclare   // Optionalglobal   // 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);endrule "shop-rule-02"    when        $order:CustomerOrder(purchaseQuantity == 2)    then        System.out.println("匹配规定 shop-rule-02");        $order.setDiscount(0.98);endrule "shop-rule-03"    when        $order:CustomerOrder(purchaseQuantity >= 3)    then        System.out.println("匹配规定 shop-rule-03");        $order.setDiscount(0.85);end

3、解释一下包名


shop-discount.drl从上图中可知是规定文件。
如果 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引擎的根本组件


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