1. Background
Recently, I am learning the rule engine drools
, here is a brief record of the entry case drools
.
2. Why learn drools
Suppose we have the following scenario:
在我们到商店购买衣服的时候,经常会发生这样的事情,购买1件不打折,购买2件打0.98折,购买3件级以上打0.85折。
So if we want to achieve the above functions in the code, do we need to write if ... else
statements, assuming that the later rules have changed, do we need to modify these if ... else
statements, and then the program is redeployed . This is possible, but not elegant enough. So can we write these business rules into the rule file, and then modify the rule file directly after the rule change? And drools
can achieve this function.
3. Realize the above simple discount case
1. Import the jar package
<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. Write the kmodule.xml configuration file
This configuration file needs to be placed in the resources/META-INF
directory.
<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>
Here we need to pay attention to the value of kbase
under package
, this value needs to be consistent with the value of package
in the rule file, otherwise the rule will not be found. See below.
3. Write a rule file
1. The syntax of the rule file
包名,必须放置在第一行
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. Write a rule file
The name of the rule file does not matter, for example: 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. Explain the package name
shop-discount.drl
It can be seen from the above figure that it is a rule file.
If the package name of shop-discount.drl
4376712712279985e68101c4150ad0c1--- is changed to com.huan.shop1
, the following warning will be prompted:
`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.
`
Fourth, write Java code
1. Write an order object
This object holds how many clothes the user has purchased and the corresponding discounts.
/**
* 客户购买衣服的订单,省略 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. Write test code
1. Stateless test method statelessSessionTest
rule rule 2, that is, the final call 0.98
fold.
2. Stateful test method statefulSessionTest
rule rule 3, that is, the final hit 0.85
fold.
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());
}
}
Here you need to pay attention to the difference between 有状态session
and 无状态session
.
5. Test results
So far, we have implemented a simple case using drools
.
Six, the basic components of the drools engine
1. Rules
: Business rules defined by ourselves, such as rules files written by ourselves. All rules must contain at least the condition that triggers the rule and the action specified by the rule.
2. Production memory
: The location where the rules are stored in the Drools engine.
3. Facts
: Data entered or changed into the Drools engine, the Drools engine matches the rule conditions to execute the applicable rules. If the value of the Fact object is modified in the rule, the data of the real JavaBean will also change.
For example: when we call ksession.insert(对象)
, then the inserted object can be understood as Facts
object.
4. Working memory
: The location where facts are stored in the Drools engine.
5. Pattern matcher
: Matcher, match all the rules in Rule Base with the Working memory
Fact
object in ---eeeeca868d3f3a942de98c846c49c0c2---, and the successfully matched rules will be activated and into Agenda
.
6. Agenda
: Agenda, execute the activated sorting rules in Agenda.
7. Complete code
https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-quickstart
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。