Drools规则引擎实战指南:从入门到生产应用
前言
Drools是一个开源的业务规则管理系统(BRMS),基于Java开发,提供了强大的规则引擎能力。它将业务逻辑从代码中分离出来,使业务人员可以直接维护规则,提高了系统的灵活性和可维护性。本文将深入介绍Drools的核心概念和实战应用。
一、Drools核心架构
1.1 整体架构图
sql
+----------------------------------------------------------+
| Drools Architecture |
| |
| +------------------+ +------------------+ |
| | Rules (DRL) | | Fact Model | |
| | +-----------+ | | +-----------+ | |
| | | Rule 1 | | | | Java POJO | | |
| | | Rule 2 | | | | Objects | | |
| | | Rule 3 | | | +-----------+ | |
| | +-----------+ | +--------+---------+ |
| +--------+---------+ | |
| | | |
| v v |
| +--------+--------------------------+---------+ |
| | Knowledge Base (KieBase) | |
| | +--------------------------------------+ | |
| | | Compiled Rules + Patterns | | |
| | +--------------------------------------+ | |
| +--------+---------------------------------+ | |
| | | |
| v | |
| +--------+---------------------------------+ | |
| | Working Memory (KieSession) | | |
| | +-----------------------------------+ | | |
| | | Insert Facts -> Pattern Match | | | |
| | | -> Execute Actions | | | |
| | +-----------------------------------+ | | |
| +------------------------------------------+ | |
| | |
| +------------------------------------------+ | |
| | Agenda (Rule Execution) | | |
| | Rule1 [priority=10] -> Execute | | |
| | Rule2 [priority=5] -> Execute | | |
| +------------------------------------------+ | |
+----------------------------------------------------------+
1.2 核心组件说明
lua
组件层次结构:
KieServices
|
+-- KieContainer
|
+-- KieBase (Knowledge Base)
|
+-- KieSession (Working Memory)
|
+-- Facts (业务对象)
+-- Agenda (规则执行队列)
二、快速开始
2.1 Maven依赖配置
xml
<!-- pom.xml -->
<properties>
<drools.version>8.44.0.Final</drools.version>
<spring.boot.version>3.1.5</spring.boot.version>
</properties>
<dependencies>
<!-- Drools核心依赖 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-mvel</artifactId>
<version>${drools.version}</version>
</dependency>
<!-- Decision Table支持 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>${drools.version}</version>
</dependency>
<!-- Spring Boot集成 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
</dependencies>
2.2 项目结构
css
drools-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/drools/
│ │ │ ├── config/
│ │ │ │ └── DroolsConfig.java
│ │ │ ├── model/
│ │ │ │ ├── Order.java
│ │ │ │ ├── Customer.java
│ │ │ │ └── Product.java
│ │ │ ├── service/
│ │ │ │ └── RuleService.java
│ │ │ └── DroolsApplication.java
│ │ └── resources/
│ │ ├── rules/
│ │ │ ├── discount-rules.drl
│ │ │ ├── validation-rules.drl
│ │ │ └── promotion-rules.drl
│ │ ├── META-INF/
│ │ │ └── kmodule.xml
│ │ └── application.yml
│ └── test/
└── pom.xml
三、实体模型定义
3.1 订单实体
java
package com.example.drools.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private Long orderId;
private Customer customer;
private List<OrderItem> items = new ArrayList<>();
private BigDecimal totalAmount;
private BigDecimal discountAmount = BigDecimal.ZERO;
private BigDecimal finalAmount;
private String status;
private LocalDateTime orderTime;
private String promotionCode;
// 业务方法
public void calculateTotalAmount() {
this.totalAmount = items.stream()
.map(OrderItem::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
public void applyDiscount(BigDecimal discount) {
this.discountAmount = this.discountAmount.add(discount);
this.finalAmount = this.totalAmount.subtract(this.discountAmount);
}
public int getItemCount() {
return items.stream()
.mapToInt(OrderItem::getQuantity)
.sum();
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class OrderItem {
private Product product;
private int quantity;
private BigDecimal price;
private BigDecimal subtotal;
public void calculateSubtotal() {
this.subtotal = this.price.multiply(BigDecimal.valueOf(quantity));
}
}
3.2 客户实体
java
package com.example.drools.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Customer {
private Long customerId;
private String name;
private String level; // VIP, GOLD, SILVER, NORMAL
private int loyaltyPoints;
private LocalDate memberSince;
private boolean isNewCustomer;
// 业务方法
public int getMemberYears() {
return LocalDate.now().getYear() - memberSince.getYear();
}
public boolean isVip() {
return "VIP".equals(level);
}
public boolean isGold() {
return "GOLD".equals(level);
}
}
3.3 产品实体
java
package com.example.drools.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private Long productId;
private String name;
private String category; // ELECTRONICS, CLOTHING, FOOD, BOOKS
private BigDecimal price;
private boolean onSale;
private int stockQuantity;
public boolean isInStock() {
return stockQuantity > 0;
}
}
四、Drools配置
4.1 KModule配置文件
xml
<!-- src/main/resources/META-INF/kmodule.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="rules" packages="rules" default="true">
<ksession name="ksession-rules" default="true" type="stateful"/>
</kbase>
</kmodule>
4.2 Spring Boot配置类
java
package com.example.drools.config;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
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 DroolsConfig {
private static final String RULES_PATH = "rules/";
@Bean
public KieContainer kieContainer() throws IOException {
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
// 加载所有规则文件
ResourcePatternResolver resourcePatternResolver =
new PathMatchingResourcePatternResolver();
Resource[] resources = resourcePatternResolver
.getResources("classpath*:" + RULES_PATH + "**/*.drl");
for (Resource resource : resources) {
kieFileSystem.write(ResourceFactory.newClassPathResource(
RULES_PATH + resource.getFilename(), "UTF-8"));
}
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
kieBuilder.buildAll();
KieModule kieModule = kieBuilder.getKieModule();
return kieServices.newKieContainer(kieModule.getReleaseId());
}
@Bean
public KieSession kieSession() throws IOException {
return kieContainer().newKieSession();
}
}
五、规则文件编写
5.1 折扣规则
java
// src/main/resources/rules/discount-rules.drl
package rules
import com.example.drools.model.Order
import com.example.drools.model.Customer
import java.math.BigDecimal
// 规则1: VIP客户享受15%折扣
rule "VIP Customer Discount"
salience 100 // 优先级
when
$order: Order(customer.level == "VIP")
then
BigDecimal discount = $order.getTotalAmount()
.multiply(new BigDecimal("0.15"));
$order.applyDiscount(discount);
System.out.println("应用VIP折扣: " + discount);
update($order);
end
// 规则2: 金卡客户享受10%折扣
rule "Gold Customer Discount"
salience 90
when
$order: Order(customer.level == "GOLD")
then
BigDecimal discount = $order.getTotalAmount()
.multiply(new BigDecimal("0.10"));
$order.applyDiscount(discount);
System.out.println("应用金卡折扣: " + discount);
update($order);
end
// 规则3: 订单金额超过1000元享受8%折扣
rule "Large Order Discount"
salience 80
when
$order: Order(totalAmount >= 1000)
then
BigDecimal discount = $order.getTotalAmount()
.multiply(new BigDecimal("0.08"));
$order.applyDiscount(discount);
System.out.println("应用大额订单折扣: " + discount);
update($order);
end
// 规则4: 新客户首单享受5%折扣
rule "New Customer First Order Discount"
salience 70
when
$order: Order(customer.isNewCustomer == true)
then
BigDecimal discount = $order.getTotalAmount()
.multiply(new BigDecimal("0.05"));
$order.applyDiscount(discount);
System.out.println("应用新客户折扣: " + discount);
update($order);
end
// 规则5: 购买商品数量超过10件享受额外折扣
rule "Bulk Purchase Discount"
salience 60
when
$order: Order(itemCount > 10)
then
BigDecimal discount = new BigDecimal("50");
$order.applyDiscount(discount);
System.out.println("应用批量购买折扣: " + discount);
update($order);
end
5.2 促销规则
java
// src/main/resources/rules/promotion-rules.drl
package rules
import com.example.drools.model.Order
import com.example.drools.model.OrderItem
import com.example.drools.model.Product
import java.math.BigDecimal
import java.time.LocalDateTime
import java.time.LocalTime
// 规则1: 电子产品类别促销 - 买2送1
rule "Electronics Buy 2 Get 1 Free"
when
$order: Order()
$item: OrderItem(product.category == "ELECTRONICS", quantity >= 2) from $order.items
then
int freeItems = $item.getQuantity() / 2;
BigDecimal discount = $item.getPrice()
.multiply(BigDecimal.valueOf(freeItems));
$order.applyDiscount(discount);
System.out.println("电子产品买2送1,优惠: " + discount);
update($order);
end
// 规则2: 促销码应用
rule "Promotion Code - SAVE20"
when
$order: Order(promotionCode == "SAVE20")
then
BigDecimal discount = $order.getTotalAmount()
.multiply(new BigDecimal("0.20"));
$order.applyDiscount(discount);
System.out.println("应用促销码SAVE20,优惠: " + discount);
update($order);
end
// 规则3: 限时促销 - 每天10-12点额外9折
rule "Time Limited Promotion"
when
$order: Order()
eval(LocalTime.now().isAfter(LocalTime.of(10, 0)) &&
LocalTime.now().isBefore(LocalTime.of(12, 0)))
then
BigDecimal discount = $order.getTotalAmount()
.multiply(new BigDecimal("0.10"));
$order.applyDiscount(discount);
System.out.println("应用限时促销,优惠: " + discount);
update($order);
end
// 规则4: 满减活动 - 满500减50
rule "Full Reduction 500-50"
when
$order: Order(totalAmount >= 500)
then
BigDecimal discount = new BigDecimal("50");
$order.applyDiscount(discount);
System.out.println("满500减50,优惠: " + discount);
update($order);
end
// 规则5: 特定品类组合优惠
rule "Category Combo Discount"
when
$order: Order()
exists(OrderItem(product.category == "ELECTRONICS") from $order.items)
exists(OrderItem(product.category == "BOOKS") from $order.items)
then
BigDecimal discount = new BigDecimal("30");
$order.applyDiscount(discount);
System.out.println("电子产品+图书组合优惠: " + discount);
update($order);
end
5.3 验证规则
java
// src/main/resources/rules/validation-rules.drl
package rules
import com.example.drools.model.Order
import com.example.drools.model.OrderItem
import com.example.drools.model.Product
global java.util.List validationErrors
// 规则1: 库存验证
rule "Check Product Stock"
when
$order: Order()
$item: OrderItem(product.stockQuantity < quantity) from $order.items
then
validationErrors.add("产品 " + $item.getProduct().getName() +
" 库存不足,库存: " + $item.getProduct().getStockQuantity() +
",需求: " + $item.getQuantity());
System.out.println("库存验证失败: " + $item.getProduct().getName());
end
// 规则2: 订单金额验证
rule "Minimum Order Amount"
when
$order: Order(totalAmount < 10)
then
validationErrors.add("订单金额不能少于10元");
System.out.println("订单金额验证失败");
end
// 规则3: 订单项数量验证
rule "Maximum Item Quantity"
when
$order: Order()
$item: OrderItem(quantity > 100) from $order.items
then
validationErrors.add("单个商品购买数量不能超过100件");
System.out.println("商品数量验证失败");
end
// 规则4: VIP客户特殊权限
rule "VIP Special Product Access"
when
$order: Order(customer.level != "VIP")
$item: OrderItem(product.name matches ".*VIP专享.*") from $order.items
then
validationErrors.add("VIP专享商品仅限VIP客户购买");
System.out.println("VIP权限验证失败");
end
六、规则服务实现
6.1 规则引擎服务
java
package com.example.drools.service;
import com.example.drools.model.Order;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class RuleService {
private final KieContainer kieContainer;
public RuleService(KieContainer kieContainer) {
this.kieContainer = kieContainer;
}
/**
* 执行折扣规则
*/
public Order applyDiscountRules(Order order) {
KieSession kieSession = kieContainer.newKieSession();
try {
log.info("开始执行折扣规则,订单ID: {}", order.getOrderId());
// 插入事实对象
kieSession.insert(order);
// 触发所有规则
int firedRules = kieSession.fireAllRules();
log.info("执行了 {} 条规则", firedRules);
log.info("原始金额: {}, 折扣金额: {}, 最终金额: {}",
order.getTotalAmount(),
order.getDiscountAmount(),
order.getFinalAmount());
return order;
} finally {
kieSession.dispose();
}
}
/**
* 验证订单
*/
public List<String> validateOrder(Order order) {
KieSession kieSession = kieContainer.newKieSession();
List<String> validationErrors = new ArrayList<>();
try {
log.info("开始验证订单,订单ID: {}", order.getOrderId());
// 设置全局变量
kieSession.setGlobal("validationErrors", validationErrors);
// 插入事实对象
kieSession.insert(order);
order.getItems().forEach(kieSession::insert);
// 触发所有规则
kieSession.fireAllRules();
if (validationErrors.isEmpty()) {
log.info("订单验证通过");
} else {
log.warn("订单验证失败: {}", validationErrors);
}
return validationErrors;
} finally {
kieSession.dispose();
}
}
/**
* 执行指定规则
*/
public Order applySpecificRule(Order order, String ruleName) {
KieSession kieSession = kieContainer.newKieSession();
try {
log.info("执行指定规则: {}", ruleName);
kieSession.insert(order);
// 执行指定的规则
kieSession.getAgenda().getAgendaGroup(ruleName).setFocus();
kieSession.fireAllRules();
return order;
} finally {
kieSession.dispose();
}
}
/**
* 批量处理订单
*/
public List<Order> batchProcessOrders(List<Order> orders) {
KieSession kieSession = kieContainer.newKieSession();
try {
log.info("批量处理 {} 个订单", orders.size());
// 插入所有订单
orders.forEach(kieSession::insert);
// 触发所有规则
int firedRules = kieSession.fireAllRules();
log.info("总共执行了 {} 条规则", firedRules);
return orders;
} finally {
kieSession.dispose();
}
}
}
6.2 订单处理服务
java
package com.example.drools.service;
import com.example.drools.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Slf4j
@Service
public class OrderService {
private final RuleService ruleService;
public OrderService(RuleService ruleService) {
this.ruleService = ruleService;
}
/**
* 创建订单并应用规则
*/
public Order createOrder(Customer customer, List<OrderItem> items, String promotionCode) {
// 创建订单
Order order = new Order();
order.setOrderId(System.currentTimeMillis());
order.setCustomer(customer);
order.setItems(items);
order.setOrderTime(LocalDateTime.now());
order.setPromotionCode(promotionCode);
order.setStatus("CREATED");
// 计算小计
items.forEach(OrderItem::calculateSubtotal);
// 计算总金额
order.calculateTotalAmount();
order.setFinalAmount(order.getTotalAmount());
log.info("创建订单: {}, 客户: {}, 原始金额: {}",
order.getOrderId(),
customer.getName(),
order.getTotalAmount());
// 验证订单
List<String> errors = ruleService.validateOrder(order);
if (!errors.isEmpty()) {
order.setStatus("VALIDATION_FAILED");
log.error("订单验证失败: {}", errors);
return order;
}
// 应用折扣规则
order = ruleService.applyDiscountRules(order);
order.setStatus("CONFIRMED");
log.info("订单处理完成: {}, 最终金额: {}, 节省: {}",
order.getOrderId(),
order.getFinalAmount(),
order.getDiscountAmount());
return order;
}
/**
* 计算订单优惠预览
*/
public Order previewDiscount(Order order) {
// 创建订单副本用于预览
Order previewOrder = cloneOrder(order);
return ruleService.applyDiscountRules(previewOrder);
}
private Order cloneOrder(Order order) {
Order clone = new Order();
clone.setOrderId(order.getOrderId());
clone.setCustomer(order.getCustomer());
clone.setItems(order.getItems());
clone.setTotalAmount(order.getTotalAmount());
clone.setFinalAmount(order.getTotalAmount());
clone.setPromotionCode(order.getPromotionCode());
return clone;
}
}
七、实际应用场景
7.1 电商促销系统
java
package com.example.drools.scenario;
import com.example.drools.model.*;
import com.example.drools.service.OrderService;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
@Component
public class EcommerceScenario {
private final OrderService orderService;
public EcommerceScenario(OrderService orderService) {
this.orderService = orderService;
}
/**
* 场景1: VIP客户购买电子产品
*/
public void vipCustomerPurchase() {
// 创建VIP客户
Customer vipCustomer = new Customer(
1L,
"张三",
"VIP",
5000,
LocalDate.of(2020, 1, 1),
false
);
// 创建商品
Product laptop = new Product(
101L,
"笔记本电脑",
"ELECTRONICS",
new BigDecimal("5999"),
true,
50
);
Product mouse = new Product(
102L,
"无线鼠标",
"ELECTRONICS",
new BigDecimal("199"),
true,
100
);
// 创建订单项
OrderItem item1 = new OrderItem(laptop, 1, laptop.getPrice(), BigDecimal.ZERO);
OrderItem item2 = new OrderItem(mouse, 2, mouse.getPrice(), BigDecimal.ZERO);
List<OrderItem> items = Arrays.asList(item1, item2);
// 处理订单
Order order = orderService.createOrder(vipCustomer, items, null);
printOrderSummary(order);
}
/**
* 场景2: 新客户首单
*/
public void newCustomerFirstOrder() {
Customer newCustomer = new Customer(
2L,
"李四",
"NORMAL",
0,
LocalDate.now(),
true
);
Product book = new Product(
201L,
"Java编程思想",
"BOOKS",
new BigDecimal("89"),
false,
200
);
OrderItem item = new OrderItem(book, 3, book.getPrice(), BigDecimal.ZERO);
Order order = orderService.createOrder(newCustomer, Arrays.asList(item), null);
printOrderSummary(order);
}
/**
* 场景3: 促销码+大额订单
*/
public void promotionCodePurchase() {
Customer goldCustomer = new Customer(
3L,
"王五",
"GOLD",
2000,
LocalDate.of(2022, 6, 1),
false
);
Product phone = new Product(
301L,
"智能手机",
"ELECTRONICS",
new BigDecimal("3999"),
true,
30
);
OrderItem item = new OrderItem(phone, 1, phone.getPrice(), BigDecimal.ZERO);
Order order = orderService.createOrder(
goldCustomer,
Arrays.asList(item),
"SAVE20"
);
printOrderSummary(order);
}
private void printOrderSummary(Order order) {
System.out.println("\n========== 订单摘要 ==========");
System.out.println("订单ID: " + order.getOrderId());
System.out.println("客户: " + order.getCustomer().getName() +
" (" + order.getCustomer().getLevel() + ")");
System.out.println("商品数量: " + order.getItemCount());
System.out.println("原始金额: ¥" + order.getTotalAmount());
System.out.println("优惠金额: ¥" + order.getDiscountAmount());
System.out.println("最终金额: ¥" + order.getFinalAmount());
System.out.println("状态: " + order.getStatus());
System.out.println("=============================\n");
}
}
7.2 规则执行流程
lua
订单处理流程:
+------------------+
| 创建订单对象 |
+--------+---------+
|
v
+--------+---------+
| 计算商品小计 |
+--------+---------+
|
v
+--------+---------+
| 计算订单总额 |
+--------+---------+
|
v
+--------+---------+
| 订单验证规则 |
| - 库存检查 |
| - 金额检查 |
| - 权限检查 |
+--------+---------+
|
+----+----+
| |
YES NO
| |
v v
+---+----+ +--+------------+
|折扣规则 | | 返回验证错误 |
|执行 | +--------------+
+---+----+
|
v
+---+------------+
| 按优先级执行: |
| 1. VIP折扣 |
| 2. 会员折扣 |
| 3. 大额折扣 |
| 4. 促销折扣 |
+---+------------+
|
v
+---+------------+
| 计算最终金额 |
+---+------------+
|
v
+---+------------+
| 返回订单结果 |
+----------------+
八、Decision Table决策表
8.1 Excel决策表
java
// 决策表结构 (在Excel中)
/*
RuleTable DiscountDecisionTable
CONDITION CONDITION CONDITION ACTION
客户等级 订单金额 商品数量 折扣率
customer.level totalAmount itemCount discountRate
VIP >1000 >5 0.20
VIP >500 >0 0.15
GOLD >1000 >5 0.15
GOLD >500 >0 0.10
SILVER >1000 >10 0.10
SILVER >500 >0 0.08
NORMAL >2000 >20 0.08
NORMAL >1000 >10 0.05
*/
8.2 加载Decision Table
java
package com.example.drools.config;
import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.InputStream;
@Configuration
public class DecisionTableConfig {
@Bean
public KieContainer decisionTableContainer() {
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
// 编译Excel决策表
InputStream template = getClass()
.getResourceAsStream("/decision-tables/discount-table.xls");
SpreadsheetCompiler compiler = new SpreadsheetCompiler();
String drl = compiler.compile(template, InputType.XLS);
kieFileSystem.write("src/main/resources/discount-table.drl", drl);
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
kieBuilder.buildAll();
KieModule kieModule = kieBuilder.getKieModule();
return kieServices.newKieContainer(kieModule.getReleaseId());
}
}
九、监控与调试
9.1 规则执行监听器
java
package com.example.drools.listener;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.event.rule.*;
@Slf4j
public class RuleExecutionListener extends DefaultAgendaEventListener {
@Override
public void beforeMatchFired(BeforeMatchFiredEvent event) {
String ruleName = event.getMatch().getRule().getName();
log.info("准备执行规则: {}", ruleName);
}
@Override
public void afterMatchFired(AfterMatchFiredEvent event) {
String ruleName = event.getMatch().getRule().getName();
log.info("规则执行完成: {}", ruleName);
}
@Override
public void matchCreated(MatchCreatedEvent event) {
String ruleName = event.getMatch().getRule().getName();
log.debug("规则匹配成功: {}", ruleName);
}
@Override
public void matchCancelled(MatchCancelledEvent event) {
String ruleName = event.getMatch().getRule().getName();
log.debug("规则匹配取消: {}", ruleName);
}
}
9.2 使用监听器
java
package com.example.drools.service;
import com.example.drools.listener.RuleExecutionListener;
import com.example.drools.model.Order;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.stereotype.Service;
@Service
public class MonitoredRuleService {
private final KieContainer kieContainer;
public MonitoredRuleService(KieContainer kieContainer) {
this.kieContainer = kieContainer;
}
public Order executeWithMonitoring(Order order) {
KieSession kieSession = kieContainer.newKieSession();
try {
// 添加监听器
kieSession.addEventListener(new RuleExecutionListener());
// 执行规则
kieSession.insert(order);
kieSession.fireAllRules();
return order;
} finally {
kieSession.dispose();
}
}
}
十、性能优化
10.1 规则优化建议
java
// 优化前 - 性能较差
rule "Bad Performance Rule"
when
$order: Order()
$customer: Customer() from $order.customer
$item: OrderItem() from $order.items
then
// 复杂计算
end
// 优化后 - 性能更好
rule "Good Performance Rule"
salience 100
when
$order: Order(
customer.level == "VIP",
totalAmount > 1000
)
then
// 简化逻辑
$order.applyDiscount($order.getTotalAmount().multiply(new BigDecimal("0.15")));
end
10.2 KieSession管理
java
package com.example.drools.service;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.util.concurrent.ConcurrentLinkedQueue;
@Component
public class KieSessionPool {
private final KieContainer kieContainer;
private final ConcurrentLinkedQueue<KieSession> sessionPool;
private static final int POOL_SIZE = 10;
public KieSessionPool(KieContainer kieContainer) {
this.kieContainer = kieContainer;
this.sessionPool = new ConcurrentLinkedQueue<>();
initializePool();
}
private void initializePool() {
for (int i = 0; i < POOL_SIZE; i++) {
sessionPool.offer(kieContainer.newKieSession());
}
}
public KieSession getSession() {
KieSession session = sessionPool.poll();
if (session == null) {
return kieContainer.newKieSession();
}
return session;
}
public void returnSession(KieSession session) {
if (session != null) {
session.dispose();
sessionPool.offer(kieContainer.newKieSession());
}
}
@PreDestroy
public void cleanup() {
sessionPool.forEach(KieSession::dispose);
}
}
十一、测试用例
11.1 单元测试
java
package com.example.drools;
import com.example.drools.model.*;
import com.example.drools.service.RuleService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class DroolsRuleTest {
@Autowired
private RuleService ruleService;
private Order testOrder;
private Customer vipCustomer;
@BeforeEach
void setUp() {
vipCustomer = new Customer(
1L, "测试VIP", "VIP", 5000,
LocalDate.of(2020, 1, 1), false
);
Product product = new Product(
1L, "测试商品", "ELECTRONICS",
new BigDecimal("1000"), true, 100
);
OrderItem item = new OrderItem(
product, 2, product.getPrice(), BigDecimal.ZERO
);
item.calculateSubtotal();
testOrder = new Order();
testOrder.setOrderId(1L);
testOrder.setCustomer(vipCustomer);
testOrder.setItems(Arrays.asList(item));
testOrder.calculateTotalAmount();
testOrder.setFinalAmount(testOrder.getTotalAmount());
}
@Test
void testVipDiscount() {
Order result = ruleService.applyDiscountRules(testOrder);
assertTrue(result.getDiscountAmount().compareTo(BigDecimal.ZERO) > 0,
"VIP客户应该有折扣");
assertTrue(result.getFinalAmount().compareTo(result.getTotalAmount()) < 0,
"最终金额应该小于原始金额");
}
@Test
void testLargeOrderDiscount() {
Product expensiveProduct = new Product(
2L, "昂贵商品", "ELECTRONICS",
new BigDecimal("1500"), true, 50
);
OrderItem item = new OrderItem(
expensiveProduct, 1, expensiveProduct.getPrice(), BigDecimal.ZERO
);
item.calculateSubtotal();
testOrder.setItems(Arrays.asList(item));
testOrder.calculateTotalAmount();
Order result = ruleService.applyDiscountRules(testOrder);
assertTrue(result.getDiscountAmount().compareTo(BigDecimal.ZERO) > 0,
"大额订单应该有折扣");
}
@Test
void testOrderValidation() {
Product outOfStock = new Product(
3L, "缺货商品", "ELECTRONICS",
new BigDecimal("500"), true, 0
);
OrderItem item = new OrderItem(outOfStock, 1, outOfStock.getPrice(), BigDecimal.ZERO);
testOrder.setItems(Arrays.asList(item));
List<String> errors = ruleService.validateOrder(testOrder);
assertFalse(errors.isEmpty(), "应该有验证错误");
assertTrue(errors.stream().anyMatch(e -> e.contains("库存")),
"应该包含库存错误");
}
}
十二、总结
本文全面介绍了Drools规则引擎的实战应用:
- 核心架构:KieBase、KieSession、Agenda工作原理
- 规则编写:DRL语法、条件匹配、动作执行
- Spring集成:配置管理、依赖注入
- 实战场景:电商促销、订单折扣、库存验证
- 高级特性:Decision Table、规则监听、性能优化
- 最佳实践:规则设计、Session管理、测试策略
Drools将业务规则与代码解耦,使规则更易维护和扩展,是企业级应用的理想选择。