共计 5590 个字符,预计需要花费 14 分钟才能阅读完成。
一、背景
最近在学习规定引擎 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>
此处咱们须要关注一下 kbase
下 package
的值,这个值须要和规定文件中的 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、解释一下包名
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