一、概述
drools
是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、不用重启服务器就可以在线上环境立即生效。
规则引擎的诞生
针对复杂的业务规则代码处理,往往存在一下问题:
1、硬编码实现业务规则难以维护;
2、硬编码实现业务规则难以应对变化;
3、业务规则发生变化需要修改代码,重启服务后才能生效;
于是规则引擎Drools便诞生了
规则引擎的优势
1、业务规则与系统代码分离,实现业务规则的集中管理
2、在不重启服务的情况下可随时对业务规则进行扩展和维护
3、可以动态修改业务规则,从而快速响应需求变更
4、规则引擎是相对独立的,只关心业务规则,使得业务分析人员也可以参与编辑、维护系统的业务规则
5、减少了硬编码业务规则的成本和风险
6、使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单
drools组成结构
Working Memory
:工作内存,drools规则引擎会从Working Memory中获取数据并和规则文件中定义的规则进行模式匹配,所以我们开发的应用程序只需要将我们的数据插入到Working Memory中即可;Rule Base
:规则库,我们在规则文件中定义的规则都会被加载到规则库中Inference Engine
:推理引擎
二、基础语法
关键字 | 含义 |
---|---|
package | 包路径。建议该路径写我们的逻辑代码路径 |
dialect | 语言,指定目前系统语言 |
import | 导入规则文件需要使用到的外部变量,和Java的引入一样 |
rule | 规则体,以rule开头,以end结尾。后面一般会跟一只字符串的值,建议字符串的值写规则名称,或者描述什么规则 |
when | 条件,这里如果为空,则表示eval(true) |
then | 符合条件之后执行的逻辑 |
变量名 | 以$符号开始后面跟我们Java中的对象 |
注意:
- 注释的使用,单行// 多行/**/
- 单个drl文件可以书写多个规则
- 条件允许为空
三、实例
规则引擎的使用分为两种:
- 第一种:直接加载
classPath
下的规则(即drl
文件) - 第二种:编程方式动态加载规则
resources
下增加文件/META-INF/kmodule.xml
(这里空,后面newKieSession()
不给参数就默认就行了)
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
</kmodule>
实体
@Data
public class Person {
private String name;
private int age;
}
第一种:直接加载classPath
下的规则(即drl
文件)
- 测试代码
@Test
void contextLoads() {
KieServices kieServices = KieServices.Factory.get();
// 加载classPath下的规则
container = kieServices.getKieClasspathContainer();
// 编译规则,若通过则生成会话
statefulKieSession = container.newKieSession();
Person person = new Person();
person.setAge(12);
person.setName("Test");
statefulKieSession.insert(person);
// 执行规则引擎
statefulKieSession.fireAllRules();
statefulKieSession.dispose();
}
- 规则文件
- 规则文件
test.drl
import com.lw.pojo.Person
dialect "mvel"
rule "age"
when
$person : Person(age<16 || age>50)
then
System.out.println("这个人的年龄不符合要求!(基于规则文件)");
end
第二种:编程方式动态加载规则
@Test
public void ruleStringTest() throws Exception {
// 基于此可以通过ftl,动态生成,然后保存到数据库,初始化Spring容器时加载就可以了
String myRule = "import com.lw.pojo.Person\n" +
"\n" +
"dialect \"mvel\"\n" +
"\n" +
"rule \"age\"\n" +
" when\n" +
" $person : Person(age<16 || age>50)\n" +
" then\n" +
" System.out.println(\"这个人的年龄不符合要求!(基于动态加载)\");\n" +
"end\n";
KieHelper helper = new KieHelper();
helper.addContent(myRule, ResourceType.DRL);
KieSession ksession = helper.build().newKieSession();
Person person = new Person();
person.setAge(12);
person.setName("Test");
ksession.insert(person);
ksession.fireAllRules();
ksession.dispose();
}
第二种(扩展)
- 初始化Spring容器时加载规则drl到容器中
/**
* 初始化Spring容器时加载规则drl到容器中
*/
@Configuration
public class KiaSessionConfig {
private static final String RULES_PATH = "rules/";
@Bean
public KieFileSystem kieFileSystem() throws IOException {
// KieFileSystem 是一个内存文件系统,用于以编程方式定义, 组成 KieModule 的资源
KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
for (Resource file : getRuleFiles()) {
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
}
return kieFileSystem;
}
private Resource[] getRuleFiles() throws IOException {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
final Resource[] resources = resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
return resources;
}
@Bean
public KieContainer kieContainer() throws IOException {
final KieRepository kieRepository = getKieServices().getRepository();
kieRepository.addKieModule(new KieModule() {
public ReleaseId getReleaseId() {
return kieRepository.getDefaultReleaseId();
}
});
// 解析drl文件内容并构建规则
KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
}
private KieServices getKieServices() {
return KieServices.Factory.get();
}
// 将ftl动态生成的规则存储到数据库时,用于初始化加载全部规则
@Bean
public KieBase kieBase() {
/**
* KieBase 是所有应用程序知识定义的存储库。
* 它将包含规则、流程、功能、类型模型。
* KieBase 本身不包含运行时数据,而是从 KieBase 创建会话可以在其中插入数据并启动流程实例。
*/
KieBase kieBase = getKieServices().getKieClasspathContainer().getKieBase();
// 查询出所有规则
// List<Rule> rules = ruleMapper.selectAll();
// // 分别编译(分别处理编译错误)
// rules.forEach(item ->
// 构建通过编程方式kieHelper.addContent(definition, ResourceType.DRL);
// DroolsUtil.buildRule(kieBase, item.getName(), item.getDefinition())
// );`在这里插入代码片`
return kieBase;
}
// 注册到bean中,方便自动装配直接使用
@Bean
public KieSession kieSession() throws IOException {
// 为这个 KieContainer 创建默认的 KieSession
return kieContainer().newKieSession();
}
}
- 实体
@Data
public class User {
private String name;
private String address;
private String sex;
}
- 规则
rule.drl
package com.lw.rules
import com.lw.pojo.User
dialect "java"
rule "address eq bsg"
when
$user: User(address == "北京" || address == "上海" || address == "广东")
then
System.out.println($user.getName() + "在北上广相信眼泪");
end
rule "address eq man"
when
$user: User(sex == "1")
then
System.out.println($user.getName() + "是男性");
end
rule "address eq woman"
when
$user: User(sex == "2")
then
System.out.println($user.getName() + "是女性");
end
- 执行规则
@Test
void test1() {
User user = new User();
user.setAddress("广东");
user.setName("wh");
user.setSex("1");
// 插入用户
session.insert(user);
// 执行规则
session.fireAllRules();
}
代码放置在gitee
,仓库地址:https://gitee.com/lwstudy/study