1. Drools引擎简介

1、基础简介

Drools是一个基于java的规则引擎,开源的,可以将复杂多变的规则从硬编码中解放出来,以规则脚本的形式存放在文件中,使得规则的变更不需要修正代码重启机器就可以立即在线上环境生效。具有易于访问企业策略、易于调整以及易于管理的特点,作为开源业务规则引擎,符合业内标准,速度快、效率高。

2、规则语法

(1)、演示drl文件格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import com.xxyx.mvel.entity.OrderRequest;
import com.xxyx.mvel.enums.CustomerType;
global com.xxyx.mvel.entity.OrderDiscount orderDiscount;

dialect "mvel"

// 规则1: 根据年龄判断
rule "Age based discount"
when
// 当客户年龄在20岁以下或者50岁以上
OrderRequest(age < 20 || age > 50)
then
// 则添加10%的折扣
System.out.println("==========Adding 10% discount for Kids/ senior customer=============");
orderDiscount.setDiscount(orderDiscount.getDiscount() + 10);
end

// 规则2: 根据客户类型的规则
rule "Customer type based discount - Loyal customer"
when
// 当客户类型是LOYAL
OrderRequest(customerType.getValue == "LOYAL")
then
// 则增加5%的折扣
System.out.println("==========Adding 5% discount for LOYAL customer=============");
orderDiscount.setDiscount(orderDiscount.getDiscount() + 5);
end

rule "Customer type based discount - others"
when
OrderRequest(customerType.getValue != "LOYAL")
then
System.out.println("==========Adding 3% discount for NEW or DISSATISFIED customer=============");
orderDiscount.setDiscount(orderDiscount.getDiscount() + 3);
end

rule "Amount based discount"
when
OrderRequest(amount > 1000)
then
System.out.println("==========Adding 5% discount for amount more than 1000$=============");
orderDiscount.setDiscount(orderDiscount.getDiscount() + 5);
end

(2)、语法说明

· 文件格式

​ 可以 .drl、xml文件,也可以Java代码块硬编码;

· package

​ 规则文件中,package是必须定义的,必须放在规则文件第一行;

· import

​ 规则文件使用到的外部变量,可以是一个类,也可以是类中的可访问的静态方法;

· rule

​ 定义一个规则。paramcheck1规则名。规则通常包含三个部分:属性、条件、结果;

二、整合SpringBoot框架

1、项目结构

SpringBoot2 整合 Drools规则引擎,实现高效的业务规则

image-20230704133816143

2、核心依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 规则引擎 3个 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.74.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.74.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>7.74.0.Final</version>
</dependency>
<!-- Mvel表达式 -->
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.4.12.Final</version>
</dependency>

3、配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.xxyx.mvel.config;

import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
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;

/**
* @author: wanrun
* @date: 2023/7/4
* @description:
*/
@Configuration
public class DroolsAutoConfiguration {

private static final String RULES_PATH = "rules/";

private KieServices getKieServices() {
return KieServices.Factory.get();
}

@Bean
@ConditionalOnMissingBean(KieFileSystem.class)
public KieFileSystem kieFileSystem() throws IOException {
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();
return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
}

@Bean
@ConditionalOnMissingBean(KieContainer.class)
public KieContainer kieContainer() throws IOException {
final KieRepository kieRepository = getKieServices().getRepository();

kieRepository.addKieModule(() -> kieRepository.getDefaultReleaseId());

KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
kieBuilder.buildAll();

KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());

return kieContainer;
}

@Bean
@ConditionalOnMissingBean(KieBase.class)
public KieBase kieBase() throws IOException {
return kieContainer().getKieBase();
}
}

这样环境整合就完成了。

1、规则调用接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import com.alibaba.fastjson2.JSON;
import com.xxyx.mvel.MvelApplication;
import com.xxyx.mvel.entity.OrderDiscount;
import com.xxyx.mvel.entity.OrderRequest;
import com.xxyx.mvel.enums.CustomerType;
import com.xxyx.mvel.service.OrderDiscountService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@Slf4j
@SpringBootTest(classes = MvelApplication.class)
@RunWith(SpringRunner.class)
public class MvelApplicationTest {

@Autowired
private OrderDiscountService orderDiscountService;

//drools表达式
@Test
@DisplayName("drools表达式")
public void droolsExpr() {
OrderRequest orderRequest = new OrderRequest("123456",19, 20000, CustomerType.LOYAL);
OrderDiscount discount = orderDiscountService.getDiscount(orderRequest);
System.out.println(JSON.toJSONString(discount));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.xxyx.mvel.service;

import com.xxyx.mvel.entity.OrderDiscount;
import com.xxyx.mvel.entity.OrderRequest;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* @author: wanrun
* @date: 2023/7/4
* @description:
*/
@Service
public class OrderDiscountService {

@Autowired
private KieContainer kieContainer;

public OrderDiscount getDiscount(OrderRequest orderRequest) {
OrderDiscount orderDiscount = new OrderDiscount();
// 开启会话
KieSession kieSession = kieContainer.newKieSession();
// 设置折扣对象
kieSession.setGlobal("orderDiscount", orderDiscount);
// 设置订单对象
kieSession.insert(orderRequest);
// 触发规则
kieSession.fireAllRules();
//或者 通过规则过滤器实现只执行指定规则
//kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("Age based discount"));
// 中止会话
kieSession.dispose();
return orderDiscount;
}
}

规则说明:

A、当客户年龄在20岁以下或者50岁以上,+10%;

B、当客户类型是LOYAL,+5%。

C、当客户类型不是LOYAL,+3%。

D、当订单金额 > 1000,+5%。

最终输出:

==========Adding 10% discount for Kids/ senior customer=============
==========Adding 5% discount for LOYAL customer=============
==========Adding 5% discount for amount more than 1000$=============
{“discount”:20}