当前位置: 首页 > news >正文

Drools在java中的使用

一、基础

  • 依赖引入
<!-- Drools 核心依赖 -->
<!--drools-core:这是Drools规则引擎的核心包,包含了Drools规则引擎的基本功能,如规则存储、规则执行等核心机制-->
<dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>8.44.0.Final</version>
</dependency>
<!--drools-compiler:这个包提供了对Drools规则文件(如DRL文件)的编译功能,将规则文件编译成可以在运行时执行的规则-->
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>8.44.0.Final</version>
</dependency>
<!--drools-decisiontables:支持从Excel或CSV文件中加载规则,这些文件通常被称为决策表,它们提供了一种直观的方式来定义复杂的业务规则-->
<dependency><groupId>org.drools</groupId><artifactId>drools-decisiontables</artifactId><version>8.44.0.Final</version>
</dependency>
<!--drools-mvel:Drools所需的脚本语言包,MVEL(MVFLEX Expression Language)是一个动态的表达式语言,用于在Drools规则中编写更复杂的逻辑。-->
<dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>8.44.0.Final</version>
</dependency>
<dependency><groupId>org.kie</groupId><artifactId>kie-api</artifactId><version>8.44.0.Final</version>
</dependency>
  • 创建实体
CREATE TABLE `rule_definitions` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`version` varchar(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1',`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`description` text COLLATE utf8mb4_unicode_ci,`content` longtext COLLATE utf8mb4_unicode_ci,`enabled` tinyint(1) DEFAULT '1',`creator` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updater` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '租户编号',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;INSERT INTO `rule_definitions` (`version`, `name`, `description`, `content`, `enabled`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES ('1', 'rule1', NULL, 'package rules;\r\nimport cn.iocoder.yudao.module.iscs.dal.dataobject.Person;\r\n\r\ndialect \"mvel\" // 默认就是MVEL表达式语言\r\n\r\nrule \"checkHighSalary11\"\r\nwhen\r\n    $p : Person(age > 40 && salary >= 40000)\r\nthen\r\n    System.out.println(\"rule1警告:[\" + $p.getName() + \"]薪资过高sqll!\");\r\nend\r\n\r\nrule \"checkHighSalary12\"\r\nwhen\r\n    $p : Person(age > 30 && salary >= 30000)\r\nthen\r\n    System.out.println(\"rule1警告:[\" + $p.getName() + \"]薪资过高2sqll!\");\r\nend', 1, '', '2025-10-21 14:04:01', '', '2025-10-21 16:19:18', b'0', 1);
INSERT INTO `rule_definitions` (`version`, `name`, `description`, `content`, `enabled`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES ('2', 'rule2', NULL, 'package rules;\r\nimport cn.iocoder.yudao.module.iscs.dal.dataobject.Person;\r\n\r\ndialect \"mvel\" // 默认就是MVEL表达式语言\r\n\r\nrule \"checkHighSalary\"\r\nwhen\r\n    $p : Person(age > 20 && salary >= 20000)\r\nthen\r\n    System.out.println(\"rule2警告:[\" + $p.getName() + \"]薪资过高sqll!\");\r\nend\r\n\r\nrule \"checkHighSalary2\"\r\nwhen\r\n    $p : Person(age > 10 && salary >= 10000)\r\nthen\r\n    System.out.println(\"rule2警告:[\" + $p.getName() + \"]薪资过高2sqll!\");\r\nend', 1, '', '2025-10-21 14:04:01', '', '2025-10-22 09:18:04', b'0', 1);
package cn.iocoder.yudao.module.iscs.dal.dataobject.rule;import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;/*** 角色和岗位关联 DO** @author 芋道源码*/
@TableName("rule_definitions")
@KeySequence("rule_definitions_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RuleDefinitionsDO extends BaseDO {/*** 序号ID*/@TableIdprivate Long id;/*** version*/private String version;/*** name*/private String name;/*** description*/private String description;/*** content*/private String content;/*** enabled*/private Integer enabled;}
  • 创建service
@Override
public RuleDefinitionsDO getOneByName(String name) {Assert.notBlank(name, "名称不能为空!");return getOne(Wrappers.<RuleDefinitionsDO>lambdaQuery().eq(RuleDefinitionsDO::getName, name));
}@Override
public List<RuleDefinitionsDO> getAllValidRules() {return list();
}

drl方式

  • 新增配置类
package cn.iocoder.yudao.module.iscs.config;import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DroolsConfig {@Beanpublic KieContainer kieContainer() {KieServices kieServices = KieServices.Factory.get();KieFileSystem kfs = kieServices.newKieFileSystem();// 加载 resources/rules/ 目录下的所有 .drl 文件kfs.write(ResourceFactory.newClassPathResource("rules/sample.drl", getClass().getClassLoader()));KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();Results results = kieBuilder.getResults();if (results.hasMessages()) {results.getMessages().forEach(msg -> System.err.println("&#9888;️ Rules compilation error: " + msg));}// &#9989; 正确方式:通过 ReleaseId 创建容器KieModule kieModule = kieBuilder.getKieModule();ReleaseId releaseId = kieModule.getReleaseId(); // 获取模块的唯一标识符return kieServices.newKieContainer(releaseId);   // 根据 ReleaseId 创建容器}// 以下是sql加载demo/*@Beanpublic KieContainer kieContainer2() {String ruleContent = "package rules;\n" +"import cn.iocoder.yudao.module.iscs.dal.dataobject.Person;\n" +"\n" +"dialect \"mvel\" // 默认就是MVEL表达式语言\n" +"\n" +"rule \"checkHighSalary\"\n" +"when\n" +"    $p : Person(age > 30 && salary >= 10000)\n" +"then\n" +"    System.out.println(\"警告:[\" + $p.getName() + \"]薪资过高sqldrl!\");\n" +"end\n" +"\n" +"rule \"checkHighSalary2\"\n" +"when\n" +"    $p : Person(age > 20 && salary >= 8000)\n" +"then\n" +"    System.out.println(\"警告:[\" + $p.getName() + \"]薪资过高2sqldrl!\");\n" +"end\n";// 校验规则内容不为空if (ruleContent == null || ruleContent.trim().isEmpty()) {throw new IllegalArgumentException("规则内容ruleContent不能为空");}KieServices kieServices = KieServices.Factory.get();KieFileSystem kfs = kieServices.newKieFileSystem();// 关键:将ruleContent字符串转换为Resource,写入KieFileSystem// 注意:需要指定资源路径(模拟文件结构,Drools会根据路径识别规则)String rulePath = "src/main/resources/rules/dynamic-rule.drl"; // 自定义路径,保持规则目录结构kfs.write(rulePath,ResourceFactory.newByteArrayResource(ruleContent.getBytes(StandardCharsets.UTF_8)) // 字符串转资源);// 构建规则并校验编译结果KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();Results results = kieBuilder.getResults();if (results.hasMessages(Message.Level.ERROR)) {throw new RuntimeException("规则编译失败:" + results.getMessages());}// 创建并返回KieContainerKieModule kieModule = kieBuilder.getKieModule();return kieServices.newKieContainer(kieModule.getReleaseId());}*/
}
  • 新增drl文件
    在这里插入图片描述
package rules;
import cn.iocoder.yudao.module.iscs.dal.dataobject.Person;dialect "mvel" // 默认就是MVEL表达式语言rule "checkHighSalary"
when$p : Person(age > 30 && salary >= 10000)
thenSystem.out.println("警告:[" + $p.getName() + "]薪资过高drl!");
endrule "checkHighSalary2"
when$p : Person(age > 20 && salary >= 12000)
thenSystem.out.println("警告:[" + $p.getName() + "]薪资过高2drl!");
end
  • 测试调用
  @Operation(summary = "规则--------")@PostMapping("/execute")public Person executeRule(@RequestBody Person person) {KieSession session = null; // 声明在外部以便在 finally 块中访问try {session = kieContainer.newKieSession();session.insert(person);      // 插入事实对象到工作内存session.fireAllRules();      // 触发所有匹配的规则return person;               // 返回可能已被修改的对象} catch (Exception e) {throw new RuntimeException("Failed to execute rules", e);} finally {if (session != null) {session.dispose(); // &#9989; 显式释放资源}}}

三、mysql方式

  • 新增配置类
package cn.iocoder.yudao.module.iscs.config;import cn.iocoder.yudao.module.iscs.dal.dataobject.rule.RuleDefinitionsDO;
import cn.iocoder.yudao.module.iscs.service.rule.RuleDefinitionsService;
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.builder.Message.Level;
import org.kie.api.builder.Results;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;@Component
public class DynamicRuleManager {@Autowiredprivate RuleDefinitionsService ruleDefinitionsService;// 原子引用存储当前KieContainer(线程安全)private final AtomicReference<KieContainer> currentKieContainer = new AtomicReference<>();// 记录每条规则的当前版本(key:规则唯一标识,如名称或ID;value:版本号)private Map<String, String> ruleVersionMap = new HashMap<>();public DynamicRuleManager(RuleDefinitionsService ruleDefinitionsService) {this.ruleDefinitionsService = ruleDefinitionsService;// 初始化时加载所有规则loadRuleFromDb();}/*** 从数据库加载所有规则并更新KieContainer*/public void loadRuleFromDb() {System.out.println("开始加载所有drools规则");try {// 1. 查询所有需要加载的规则(从Service获取多条规则)List<RuleDefinitionsDO> allRules = ruleDefinitionsService.getAllValidRules();if (allRules.isEmpty()) {throw new RuntimeException("数据库中未找到任何有效规则");}// 2. 检查是否有规则需要更新(版本变化、新增或删除)if (!isNeedUpdate(allRules)) {System.out.println("所有规则版本未更新,无需重新加载");return;}// 3. 构建KieFileSystem,批量写入所有规则KieServices kieServices = KieServices.Factory.get();KieFileSystem kfs = kieServices.newKieFileSystem();for (RuleDefinitionsDO rule : allRules) {String ruleName = rule.getName(); // 规则唯一标识(假设名称唯一)String drlContent = rule.getContent(); // 规则内容// 校验规则内容if (drlContent == null || drlContent.trim().isEmpty()) {throw new RuntimeException("规则[" + ruleName + "]内容为空");}// 清洗规则内容(去除可能的多余引号、转义符,避免编译错误)String cleanedContent = cleanDrlContent(drlContent);// 生成唯一虚拟路径(用规则ID+名称确保不重复)String virtualPath = "src/main/resources/rules/dynamic-" + rule.getId() + "-" + ruleName + ".drl";kfs.write(virtualPath, ResourceFactory.newByteArrayResource(cleanedContent.getBytes(StandardCharsets.UTF_8)));System.out.println("已加载规则:" + ruleName + "(版本:" + rule.getVersion() + ")");}// 4. 编译所有规则KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();Results results = kieBuilder.getResults();if (results.hasMessages(Level.ERROR)) {throw new RuntimeException("规则编译失败:" + results.getMessages());}// 5. 创建新的KieContainer并更新版本记录KieModule kieModule = kieBuilder.getKieModule();KieContainer newKieContainer = kieServices.newKieContainer(kieModule.getReleaseId());// 原子更新容器(线程安全)currentKieContainer.set(newKieContainer);// 更新版本Map(记录当前所有规则的最新版本)updateRuleVersionMap(allRules);System.out.println("所有规则更新成功,共加载 " + allRules.size() + " 条规则");} catch (Exception e) {System.err.println("规则加载失败,使用旧规则:" + e.getMessage());}}/*** 检查是否需要更新规则(任一规则版本变化、新增或删除)*/private boolean isNeedUpdate(List<RuleDefinitionsDO> newRules) {// 首次加载(版本Map为空),需要更新if (ruleVersionMap.isEmpty()) {return true;}// 检查新规则中是否有版本变化或新增规则for (RuleDefinitionsDO rule : newRules) {String ruleName = rule.getName();String newVersion = rule.getVersion();String currentVersion = ruleVersionMap.get(ruleName);// 规则新增(当前版本Map中无此规则)或版本提升,需要更新if (currentVersion == null || !currentVersion.equals(newVersion)) {return true;}}// 检查是否有规则被删除(当前版本Map中有,但新规则中无)for (String existingRuleName : ruleVersionMap.keySet()) {boolean existsInNew = newRules.stream().anyMatch(rule -> rule.getName().equals(existingRuleName));if (!existsInNew) {return true; // 有规则被删除,需要更新}}return false;}/*** 更新规则版本Map(存储当前所有规则的最新版本)*/private void updateRuleVersionMap(List<RuleDefinitionsDO> newRules) {Map<String, String> newVersionMap = new HashMap<>();for (RuleDefinitionsDO rule : newRules) {newVersionMap.put(rule.getName(), rule.getVersion());}ruleVersionMap = newVersionMap;}/*** 清洗DRL内容(去除多余引号、转义符,避免语法错误)*/private String cleanDrlContent(String drlContent) {if (drlContent == null) {return null;}return drlContent.trim().replaceAll("^\"", "") // 去除开头的双引号.replaceAll("\"$", "") // 去除结尾的双引号.replaceAll("\\\\n", "\n"); // 替换转义换行符为实际换行}/*** 提供给业务代码获取当前KieContainer的方法*/public KieContainer getCurrentKieContainer() {KieContainer container = currentKieContainer.get();if (container == null) {throw new RuntimeException("规则容器未初始化");}return container;}
}
  • 新增定时器刷新规则
package cn.iocoder.yudao.module.iscs.config;import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@EnableScheduling // 启动类上需添加该注解开启定时任务
@Component
public class RuleUpdateScheduler {private final DynamicRuleManager ruleManager;public RuleUpdateScheduler(DynamicRuleManager ruleManager) {this.ruleManager = ruleManager;}// 每10分钟检查一次(可根据实际需求调整频率)@TenantJob@Scheduled(fixedRate = 600000)public void checkAndUpdateRule() {ruleManager.loadRuleFromDb();}
}
  • 新增测试
 // 所有规则判断@Operation(summary = "规则2--------")@PostMapping("/execute2")public Person executeRule2(@RequestBody Person person) {KieContainer currentKieContainer = dynamicRuleManager.getCurrentKieContainer();KieSession session = null; // 声明在外部以便在 finally 块中访问try {session = currentKieContainer.newKieSession();session.insert(person);      // 插入事实对象到工作内存session.fireAllRules();      // 触发所有匹配的规则return person;               // 返回可能已被修改的对象} catch (Exception e) {throw new RuntimeException("Failed to execute rules", e);} finally {if (session != null) {session.dispose(); // &#9989; 显式释放资源}}}// 所有规则中匹配规则名称进行判断@Operation(summary = "规则3--------")@PostMapping("/execute3")public Person executeRule3(@RequestBody Person person) {KieContainer currentKieContainer = dynamicRuleManager.getCurrentKieContainer();KieSession session = null;try {session = currentKieContainer.newKieSession();session.insert(person); // 插入事实对象// 关键:使用AgendaFilter过滤,只执行名称为"onrule"的规则session.fireAllRules(match -> {// 获取当前规则的名称(DRL中定义的 rule "onrule" 中的名称)String ruleName = match.getRule().getName();// 只执行名称为"onrule"的规则return ruleName.contains("checkHighSalary1");});return person;} catch (Exception e) {throw new RuntimeException("Failed to execute rules", e);} finally {if (session != null) {session.dispose(); // 释放资源}}}
http://www.dtcms.com/a/524299.html

相关文章:

  • 【办公类-121-02】20251024淘宝视频红包(UIBOT点击“左箭头”“视频”“消息”切换)
  • 9 种高级 RAG 技术及其实现方法
  • 计算机网络面试核心知识点大全
  • 做网站建站现在什么传奇最火电脑版
  • C语言需要掌握的基础知识点之图
  • 做一个类似京东的网站海外注册公司
  • python舆情分析可视化系统 情感分析 微博 爬虫 scrapy爬虫技术 朴素贝叶斯分类算法大数据 计算机✅
  • 压缩与缓存调优实战指南:从0到1根治性能瓶颈(六)
  • 做百度手机网站优化点asp网站制作教程
  • element+vue3 table上下左右键切换input和select
  • 元萝卜 1.0.27| 免Root,XP模块框架,支持应用多开分身,一键微信平板模式
  • 长春企业网站seo珠海企业官网设计制作
  • MySQL 函数详细说明
  • 《Memcached 连接:深入理解与优化实践》
  • C++ EigenSolver无优化模式下报错分析
  • 数据结构——折半插入排序
  • io_uring 快吗? Postgres 17 与 18 的基准测试
  • 国产数据库替代MongoDB:政务电子证照新选择
  • 甘孜建设网站集团响应式网站建设
  • 枸杞网站建设方案2024年即将上市的手机
  • Git 版本回退 reset --mixed 命令
  • 博途DWORD中包含word ,字节,位的关系
  • Java Character 类详解
  • 【数据结构】队列“0”基础知识讲解 + 实战演练
  • 【生活】秋冬季节,鼻子很干结痂,扣掉鼻孔干痂流血,鼻塞等护理方法
  • 网站关键词公司百度关键词查询
  • 大模型通识
  • 346. 执行操作后元素的最高频率 I
  • 一些常用的linux操作指令
  • jeecg表单设计器js增强实现效果案例;点按钮出弹框,iframe嵌套,数据传输等