Drools
,是一款由 JBoss 组织提供的基于 Java 语言编写的一个开源规则引擎,核心思想是把业务规则从程序代码中分离出来,可以更加灵活地管理业务规则。
一、什么是规则引擎
规则引擎是一种计算机软件,它根据输入数据和预定义规则,自动执行特定的操作。它可以将复杂的业务逻辑和规则表示为易于理解和修改的形式,并自动化处理这些规则。
二、规则引擎应用场景
业务规则复杂 并且 频繁变动 的系统更适合使用规则引擎。
1、风险控制系统----风险贷款、风险评估
2、反欺诈项目----银行贷款、征信验证
3、决策平台系统----财务计算
4、促销平台系统----满减、消费送积分、打折
// 超市消费送积分活动,v1
// 0 < price <= 100, 积100分
// 100 < price <= 200, 积200分
// 200 < price <= 300, 积300分
程序员小明:if...else... if...else... if...else......
老板:积分换东西的人多了,花钱的人变少了,改规则--积分降一个档次
// 超市消费送积分活动,v2
// 0 < price <= 100, 积0分
// 100 < price <= 200, 积100分
// 200 < price <= 300, 积200分
程序员小明:if...else... if...else... if...else......
老板:用户积极性变差了,改规则--送的积分增加一点
// 超市消费送积分活动,v3
// 0 < price <= 100, 积50分
// 100 < price <= 200, 积150分
// 200 < price <= 300, 积250分
程序员小明:if...else... if...else... if...else......
多少元送多少积分存到数据库,下次规则再变就直接改数据库
老板:业务规则太少,给我加倍!
小明:......
// 超市消费送积分活动,积分翻倍,v4
// 0 < price <= 100, 积50分
// 100 < price <= 200, 积150分
// 200 < price <= 300, 积250分
// 300 < price <= 400, 积350分
// 400 < price <= 500, 积450分
// 500 < price <= 600, 积550分
1.再增加三组if...else...
2.重构代码用责任链模式
程序员小明:什么技术可以将业务规则和代码解耦合?不管规则如何变化,执行端都不用动。
只有规则引擎!
三、Drools 规则引擎的构成
规则引擎由三部分组成
-
Working Memory(工作内存):存放
fact
对象。 -
Rule Base(规则库):规则文件中定义的规则都会被加载到规则库。
-
Interface Engin(推理引擎)
3.1 Pattern Matcher(匹配器):将Rule Base
中的所有规则与Working Memory
中的Fact
对象进行模式匹配,匹配成功的规则将被激活并放入Agenda
中。
3.2 Agenda(议程):用于存放通过匹配器进行模式匹配后被激活的规则。
3.3 Excution Engin(执行引擎):执行Agenda
中被激活的规则。
规则引擎执行过程:
fact(事实):普通的 JavaBean
插入到 Working Memory
后的对象就是 Fact
对象,是应用和规则引擎进行数据交互的桥梁或通道。
drools API 开发完整步骤如下:
Kie: knowledge is everything, 是 jBoss 一系列项目共享的一个核心项目,主要目的就是将相关技术整合在一起,其中包含了Drools、OptaPlanner、UberFire、jBPM等技术。
四、Drools 使用方式
在项目中使用 drools 时,既可以单独使用,也可以与 spring boot 整合使用。
4.1 方式一:Drools 单独使用
导入 maven 依赖
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.73.0.Final</version>
</dependency>
创建 kmodule.xml
根据 drools 要求创建 resources/META-INF/kmodule.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="myKbase1" packages="rules" default="true">
<ksession name="myKbase1-session" default="true"/>
</kbase>
</kmodule>
编写 drl (drools rule language)规则文件
一套完整的规则文件结构:
-
package:
/src/java/resource
目录下的包名 -
import:用于导入类或者静态方法
-
global:全局变量
-
function:自定义函数
-
query:查询
-
rule ... end:规则体
package rules;
import com.kk.model.Order;
// 超市消费送积分活动
// 0 < price <= 100, 积0分
// 100 < price <= 200, 积100分
rule "score-1"
when
$order:Order(price>0 && price <= 100)
then
System.out.println("触发drools积分规则1:积0分");
$order.setScore(0);
end
rule "score-2"
when
$order:Order(price>100 && price <= 200)
then
System.out.println("触发drools积分规则2:积100分");
$order.setScore(100);
end
测试代码:
@Test
public void test() {
// 1.获取服务对象
KieServices ks = KieServices.get();
// 2.通过服务获取容器
KieContainer container = ks.getKieClasspathContainer();
// 3.通过容器获取kie session
KieSession session = container.newKieSession();
// 定义fact事实对象
Order order = new Order();
order.setPrice(120);
// 5.把fact事实对象插入工作内存
session.insert(order);
// 6.激活规则引擎,匹配所有规则,匹配成功会执行规则
session.fireAllRules();
// 7.关闭规则引擎
session.dispose();
}
测试结果:
4.2 方式二:Spring Boot 整合 Drools
导入 maven 依赖
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${kie.version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${kie.version}</version>
</dependency>
编写 drl 规则文件
package rules;
import com.kk.model.Order;
// 超市消费送积分活动
// 0 < price <= 100, 积0分
// 100 < price <= 200, 积100分
rule "score-1"
when
$order:Order(price>0 && price <= 100)
then
System.out.println("触发drools积分规则1:积0分");
$order.setScore(0);
end
rule "score-2"
when
$order:Order(price>100 && price <= 200)
then
System.out.println("触发drools积分规则2:积100分");
$order.setScore(100);
end
编写配置类
import com.kk.utils.KieUtils;
import org.kie.api.KieBase;
import org.kie.api.builder.*;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
@Configuration
public class DroolsAutoConfig {
// 规则文件存放目录.
public static final String RULES_PATH = "rules/";
// 规则库路径.
public static final String BASE_RULES_PATH = "classpath*:";
/**
* 加载指定目录下的规则文件.
*
* @return
* @throws IOException
*/
@Bean
@ConditionalOnMissingBean(KieFileSystem.class)
public KieFileSystem kieFileSystem() throws IOException {
KieFileSystem kieFileSystem = KieUtils.getKieServices().newKieFileSystem();
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resourcePatternResolver.getResources(BASE_RULES_PATH + RULES_PATH + "**/*.*");
for (Resource file : resources) {
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
}
return kieFileSystem;
}
/**
* kie 容器注册到spring容器.
*
* @return
* @throws IOException
*/
@Bean
@ConditionalOnMissingBean(KieContainer.class)
public KieContainer kieContainer() throws IOException {
final KieRepository kieRepository = KieUtils.getKieServices().getRepository();
kieRepository.addKieModule(new KieModule() {
@Override
public ReleaseId getReleaseId() {
return kieRepository.getDefaultReleaseId();
}
});
KieBuilder kieBuilder = KieUtils.getKieServices().newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
KieContainer kieContainer = KieUtils.getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
KieUtils.setKieContainer(kieContainer);
return KieUtils.getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
}
/**
* kieBase 注册到spring容器
* @return
* @throws IOException
*/
@Bean
@ConditionalOnMissingBean(KieBase.class)
public KieBase kieBase() throws IOException {
return kieContainer().getKieBase();
}
@Bean
@ConditionalOnMissingBean(KieSession.class)
public KieSession kieSession() throws IOException {
KieSession kieSession = kieContainer().newKieSession();
KieUtils.setKieSession(kieSession);
return kieSession;
}
@Bean
@ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
public KModuleBeanFactoryPostProcessor kiePostProcessor() {
return new KModuleBeanFactoryPostProcessor();
}
}
测试代码:
import com.kk.model.Order;
import org.junit.runner.RunWith;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Main.class)
public class Test {
@Autowired
private KieSession session;
@Autowired
private KieContainer container;
@Autowired
private KieBase kieBase;
@org.junit.Test
public void test1(){
Order order = new Order();
order.setPrice(50);
session.insert(order);
session.fireAllRules();
session.dispose();
}
@org.junit.Test
public void test2(){
Order order = new Order();
order.setPrice(123);
KieSession newKieSession = container.newKieSession();
newKieSession.insert(order);
newKieSession.fireAllRules();
newKieSession.dispose();
}
@org.junit.Test
public void test3(){
Order order = new Order();
order.setPrice(234);
KieSession kieSession = kieBase.newKieSession();
kieSession.insert(order);
kieSession.fireAllRules();
kieSession.dispose();
}
}
测试结果:
Drools 优缺点
优点:
- 易于维护和管理:Drools规则引擎的规则是易于编写、修改和维护的,从而使得代码更具可读性和可维护性。
- 高效性能:Drools规则引擎使用了高效的编译器和运行时引擎,可以实现快速、灵活的决策处理。
- 灵活性:Drools规则引擎提供了丰富的语言特性和扩展性,可以根据需要进行自定义配置。
- 可重用性:Drools规则引擎提供了规则库和模板等功能,使得规则可以被多次重用,从而提高开发效率和代码质量。
缺点:
- 上手难度较大:Drools规则引擎需要理解规则引擎的基本概念和语法,并具备良好的业务分析能力。
- 对于简单问题过于繁琐:Drools规则引擎在处理简单问题时可能会显得比较繁琐,不如直接写代码来得简单明了。
- 可能会影响系统性能:Drools规则引擎在运行时需要占用一定的内存和CPU资源,可能会对系统性能产生影响。