drools的简单入门案例
一、背景
最近在学习规则引擎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
的包名修改为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
八、参考文档
最新文章
- luke使用
- blueImp/jQuery file upload 的正确用法(限制上传大小和文件类型)
- iOS 一个工程中引用其他工程时编译的Architecture问题
- sqlserver函数
- Linux 阿里云挂载新分区
- I2C总线协议的总结介绍
- (转载)css垂直水平居中的整理
- 11586 - Train Tracks
- nginx location配置(URL)
- iOS ZBar扫码简单实现
- mybatis基础学习1---(配置文件和sql语句)
- API接口签名验证2
- 解决Lost connection to MySQL server during query错误方法
- P4316 绿豆蛙的归宿(期望)
- C# dynamic类型报错:“object”不包含“xxx”的定义
- 创建一个包括菜单栏,工具栏,状态栏,文本编辑部件的经典GUI应用程序的骨架
- 从DevOps到Cloud Native,应用上云姿势全解锁
- 9.png图片制作详细教程
- static关键字的内存分析
- D. Huge Strings Codeforces Round #438 by Sberbank and Barcelona Bootcamp (Div. 1 + Div. 2 combined)
热门文章
- 是否可以从一个静态(static)方法内部发出对非静态 (non-static)方法的调用?
- 1.0缓存:Login.aspx?
- 学习SVN01
- Contos 安装nodeJs环境
- 经历了源码的痛苦,掌握DRF的核心序列化器
- 项目启动的缓慢之“Build completed with 1 error and 18 warnings in 3 m 51 s”
- Java设置方法模板
- 学习webpack前的准备工作
- js如何获取iframe页面内的对象
- Hive进行数据统计时报错:org.apache.hadoop.mapreduce.v2.app.MRAppMaster: Error starting MRAppMaster