Drools学习01

article/2025/10/1 8:59:17

Drools学习

1.springboot+drools+stringTemplate

1.1主要依赖导入

<!--使用drools的主要依赖-->
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.73.0.Final</version>
</dependency>
<!--主要模板依赖-->
<dependency><groupId>org.antlr</groupId><artifactId>ST4</artifactId><version>4.3.4</version>
</dependency>

2主要方法

1.2.1 drools的工具方法

下面的大部分容器其实本质上还是key-value的HashMap
@Component
pulica class DroolsManager {private static Logger log = LoggerFactory.getLogger(DroolsManager.class);//drools服务类 后续kfs和kmm 以及 kie容器都是由它生成//这个类本身就是单例//KieServices.Factory.get();也可以生成 做了封装private static KieServices kieServices = KieServices.get();//文件系统-主要是存储 规则文件的 因此需要全局唯一一个 进行缓存,避免每次都要创建private static KieFileSystem kieFileSystem = kieServices.newKieFileSystem();//可以理解为构建kmodule.xmlprivate static KieModuleModel kieModuleModel = kieServices.newKieModuleModel();//需要全局唯一一个,如果每次价格规则都新创建一个,那么就需要销毁之前创建的看kiecontainer//public volatile static KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());public volatile static KieContainer kieContainer;/*** 创建kieContainer* 调用方法前先进行非空判定 加锁后继续进行一次判空,避免多线程重复创建*/private static void createKieContainer() {synchronized (KieContainer.class) {if (kieContainer == null) {kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());}}}/*** 判定kbase,所有一切运行的前提是kieContainer和KieBase被创建* kbase是一个关键的类,它主要存放kieSession和kbasePackageName* Map<String, KieSessionModel> kSessions;* List<String> packages;* @param kieBaseName kie基本名称* @return*/private boolean existsKieBase(String kieBaseName) {if (null == kieContainer) {log.warn("需要创建kieContainer");return false;}if (kieContainer.getKieBaseNames().contains(kieBaseName)) {return true;}log.warn("需要创建KieBase:{}", kieBaseName);return false;}//添加和修改规则方法如下
}
    /*** 添加或更新 drools规则* 更新drools规则 本质上是对KieFileSystem的更新 与其他的kie对象基本无关* 后续可以将这个方法进行拆分解耦* @param droolsRuleDTO*/public void addOrUpdateRule(DroolsRuleDTO droolsRuleDTO) {//获取kbase的名称//获取kbase包的名称//判断给kbase是否存在String kieBaseName = droolsRuleDTO.getKieBaseName();String kiePackageName = droolsRuleDTO.getKiePackageName();boolean existsKieBase = existsKieBase(kieBaseName);//该对象对应module.xml 中的kbase标签KieBaseModel kieBaseModel = null;//如果这个kbase不存在String packageName = kieBaseName + "." + kiePackageName;if (!existsKieBase) {//创建一个kBase 根据一个kiBaseName创建一个kieBaseModellog.info("first create kieBaseName");kieBaseModel = kieModuleModel.newKieBaseModel(kieBaseName);//不是默认的kieBasekieBaseModel.setDefault(false);//设置该KieBase需要加载的包路径kieBaseModel.addPackage(packageName);//设置kie的session为有状态sessionkieBaseModel.newKieSessionModel(kiePackageName + "-session")//不是默认session.setDefault(false);} else {//获取到已经存在的base对象//kieBaseName 唯一kieBaseModel = kieModuleModel.getKieBaseModels().get(kieBaseName);//获取packageList<String> packages = kieBaseModel.getPackages();//判定这个包是否已经存在//包不存在 >>//由于 这里kieSession和kieBasePackage使用同一个 name//不存在这个包意味着这个session也没有被添加//添加包的同时需要添加sessionif (!packages.contains(packageName)) {kieBaseModel.addPackage(packageName).newKieSessionModel(kiePackageName + "-session").setDefault(false);log.info("kieBase:{}添加一个新的包:{},添加一个新的session:{}-session", kieBaseName, kiePackageName, kiePackageName);} else {//存在就不用初始化他了kieBaseModel = null;}}String file =DroolCommon.DROOL_PACKAGE_PREFIX + packageName + DroolCommon.DROOL_PACKAGE_I + kiePackageName + DroolCommon.DROOL_PACKAGE_SUFFIX;log.info("加载虚拟规则文件:{}", file);//不为null则表示不是第一次if (kieBaseModel != null) {//只有当kieSession不存在的时候才需要进行 kieFileSystem的内容初始化String kmoduleXml = kieModuleModel.toXML();
//            log.info("加载kmodule.xml:[\n{}]", kmoduleXml);kieFileSystem.writeKModuleXML(kmoduleXml);}//        String ruleContent = droolsRuleDTO.getRuleContent();String ruleContent = rule(droolsRuleDTO);//2022/9/27 这里的写入应该不是追加 因为代码中并没有涉及到drl文件下的ruleName,所以本质上,无论是添加还是修改,本质上都是会涉及到所有已经存在该session下的规kieFileSystem.write(file, ruleContent);KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);//通过KieBuilder构建KieModule下所有的KieBasekieBuilder.buildAll();//获取构建过程中的结果Results results = kieBuilder.getResults();//获取构建过程中的结果List<Message> messages = results.getMessages(Message.Level.ERROR);if (null != messages && !messages.isEmpty()) {for (Message message : messages) {log.error(message.getText());}throw new RuntimeException("加载规则出现异常");}//keiContainer只有第一次时才需要创建,之后就是使用这个if (null == kieContainer) {log.info("first create kieContainer");createKieContainer();} else {//实现动态更新log.info("update kieContainer");((KieContainerImpl) kieContainer).updateToKieModule((InternalKieModule) kieBuilder.getKieModule());}}
//kiesession调用
KieSession kieSession = DroolsManager.kieContainer.newKieSession(droolsRuleDTO.getKiePackageName() + "-session");
1.2.1-1 KieSession的创建和调用的关系梳理(部分源码梳理)?
KieContainer获取kieSession源码(部分)
//这里的KieSession map 是KieContainer的
private final Map<String, KieSession> kSessions; //获取
public KieSession getKieSession(String kSessionName) 
{KieSession kieSession = (KieSession)this.kSessions.get(kSessionName);return kieSession != null ? kieSession : this.newKieSession(kSessionName);
}
//新建
public KieSession newKieSession(String kSessionName, Environment environment, KieSessionConfiguration conf) 
{KieSessionModelImpl kSessionModel = kSessionName != null ? (KieSessionModelImpl)this.getKieSessionModel(kSessionName) : (KieSessionModelImpl)this.findKieSessionModel(false);KieBase kBase = this.getKieBaseFromKieSessionModel(kSessionModel);if (kBase == null) {return null;} else {KieSession kSession = kBase.newKieSession(conf != null ? conf : this.getKieSessionConfiguration((KieSessionModel)kSessionModel), environment);this.registerNewKieSession(kSessionModel, (InternalKnowledgeBase)kBase, kSession);return kSession;}
}
public KieSessionModel getKieSessionModel(String kSessionName) {return this.kProject.getKieSessionModel(kSessionName);
}//注册
private void registerNewKieSession(KieSessionModel kSessionModel, InternalKnowledgeBase kBase, KieSession kSession) {if (Drools.isJndiAvailable()) {InjectionHelper.wireSessionComponents(kSessionModel, kSession);}this.registerLoggers(kSessionModel, kSession);this.registerCalendars(kSessionModel, kSession);((StatefulKnowledgeSessionImpl)kSession).initMBeans(this.containerId, kBase.getId(), kSessionModel.getName());this.kSessions.put(kSessionModel.getName(), kSession);
}
KieContainer的创建和Kproject的引入 (部分)
class KieServiceImpl{public KieContainer newKieContainer(String containerId, ReleaseId releaseId, ClassLoader classLoader) {InternalKieModule kieModule = (InternalKieModule)this.getRepository().getKieModule(releaseId);//创建kprojectKieProject kProject = new KieModuleKieProject(kieModule, classLoader);KieContainerImpl newContainer;if (containerId == null) {newContainer = new KieContainerImpl(UUID.randomUUID().toString(), kProject, this.getRepository(), releaseId);return newContainer;} else if (this.kContainers.get(containerId) == null) {newContainer = new KieContainerImpl(containerId, kProject, this.getRepository(), releaseId);//CurrentHashMap kContainersKieContainer check = (KieContainer)this.kContainers.putIfAbsent(containerId, newContainer);if (check == null) {return newContainer;}}}
}
KieBaseModel创建KieSession 源码
//具体调用查看 drools-compiler 源码
class KieBaseModel{public KieSessionModel newKieSessionModel(String name) {KieSessionModel kieSessionModel = new KieSessionModelImpl(this, name);Map<String, KieSessionModel> newMap = new HashMap();newMap.putAll(this.kSessions);newMap.put(kieSessionModel.getName(), kieSessionModel);this.setKSessions(newMap);return kieSessionModel;}
}

待补充……

image-20220930093235901

1.2.1-2 KModule.xml的大概规则
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"><kbase name="SimpleRuleKBase" packages="com.us.person"><ksession name="simpleRuleKSession"/></kbase>
</kmodule>
1.2.1-3 kieService和其他kie容器的类图(部分)

image-20220929163004739

待补充……

1.2.2 StringTemplate具体方法

书写关于drl的stg的前提是 了解drl文件规则组成
public String st4Util(DroolsRuleDTO droolsRuleDTO) {//通过模板文件读取URL resource = UUIDUtil.class.getClassLoader().getResource("templates/drools/people_rules.stg");STGroup group = new STGroupFile(Objects.requireNonNull(resource).getFile());//wordImport(rules,package,kieBaseName)ST stFile = group.getInstanceOf("wordImport");String kiePackageName = droolsRuleDTO.getKiePackageName();String kieBaseName = droolsRuleDTO.getKieBaseName();stFile.add("package", kiePackageName);stFile.add("kieBaseName", kieBaseName);List<DroolsRuleDTO.Rule> rules = droolsRuleDTO.getRule();for (DroolsRuleDTO.Rule rule : rules) {//ruleValue(ruleName,salience,lhs,value,remark)ST stRule = group.getInstanceOf("ruleValue");String ruleName = rule.getRuleName();String lhs = rule.getLhs();Integer salience = rule.getSalience();Object value = rule.getValue();String remark = rule.getRemark();stRule.add("ruleName", ruleName);stRule.add("salience", salience);stRule.add("lhs", lhs);stRule.add("value", value);stRule.add("remark", remark);stFile.add("rules", stRule);}return stFile.render();
}

test.stg

//stg =>   :=<< 表示一个group >>
//stg =>   wordImport groupName
wordImport(rules,package,kieBaseName) ::=<<//drl =>> package就是文件所在地址 可以是任意 但是在动态替换时 需要 xx.xx 不然报错 具体原因不清楚 也可能是别的地方引发的问题
package <kieBaseName>.<package>//drl => import就是导入包 和java中一样 rule规则体中用到哪个类 就需要导入相应的包
import com.lujy.drools_learning.pojo.User
import com.lujy.drools_learning.pojo.Account
import com.lujy.drools_learning.pojo.MyLog
import java.util.Date//drl => global 全局变量  对于引用数据类型而言 他们的变化只发生在规则内部 规则体外部还是原来的值,其他数据类型则内外同时变化
global  java.util.List logList//drl => dialect属性用于指定当前规则使用的语言类型 可同时使用多个
//drl => ;可以不使用 但是不能随意使用除了 rule和function这类方体内 别的地方最好别加 会报错
dialect "java"
dialect  "mvel"//drl => 方法体 对于某类重复方法的抽提
function MyLog addLog(User user,Account account,Number value,String remark){MyLog log=new MyLog();log.setUserId(user.getId());log.setCardId(account.getId());log.setContent(remark+":"+value+",账户余额:"+account.getMoney());log.setCardId(account.getId());//log.setCreateDate(new Date());return log;
}
//按照 换行来进行划分
<rules; separator="\n\n">
>>ruleValue(ruleName,salience,lhs,value,remark)::=<<
rule "<ruleName>"//drl => 跳出死循环no-loop true//drl => 设置优先级 Integer类型 数字越大 优先级别越高 同等优先级 按照顺序执行salience <salience>when//lhs 判定规则 可以为true$account:Account();$user:User(<lhs>);then//rhs 执行结果$account.setMoney($account.getMoney()+<value>);//更新对象的值 使得下个匹配的规则使用到的是更新过的值 不使用则还是原值update($account);MyLog log = addLog($user,$account,<value>,"<remark>");System.out.println(hasPass);logList.add(log);System.out.println("<remark>:<value>,账户余额:"+$account.getMoney());
end
>>

1.2.3测试

传入规则参数
{"createdTime":1664348350632,"id":1,"kieBaseName":"people","kiePackageName":"people01","rule":[{"lhs":"(sex==1&&age>=75)||(sex==0&&age>=70)","remark":"养老金发放","ruleName":"pension","salience":1,"value":200},{"lhs":"(sex==1&&age>=70)||(sex==0&&age>=65)","remark":"退休工资发放","ruleName":"Retirement_Salary","salience":9,"value":2000},{"lhs":"age<=16","remark":"独生子女补贴发放","ruleName":"One_child_allowance","salience":8,"value":100}]
}
传入测试参数
http://127.0.0.1:8081/drools/rule/fireRule?ruleId=1
ruleId是传入的参数Id 通过id获取到sessionName 调用对应session
{"id":12,"name":"zhangsan","age":80,"sex":1
}
//性别为男,年龄80岁的张三 符合两个规则 执行顺序为 退休工资->养老金

3.总结

规则的添加和更新其实是对KieFileSystem的变动.同一个session下,不论要添加或则修改一个或者多个规则,它的前提都是把这个范围下的所有rule提取出来,然后在这个里面修改,修改完毕后再一股脑的塞入到该session下


http://chatgpt.dhexx.cn/article/XmGJIA4Q.shtml

相关文章

Drools基础语法

Drools基础语法 规则文件构成 在使用Drools时非常重要的一个工作就是编写规则文件&#xff0c;通常规则文件的后缀为.drl。 drl是Drools Rule Language的缩写。在规则文件中编写具体的规则内容。 一套完整的规则文件内容构成如下&#xff1a; Drools支持的规则文件&#xff…

【Drools一】值得打工人学习的规则引擎Drools

本文主要对Drools的使用业务场景做简单介绍。 规则引擎 规则引擎&#xff1a;全称为业务规则管理系统&#xff0c;英文名为BRMS(即Business Rule Management System)。规则引擎的主要思想是将应用程序中的业务决策部分分离出来&#xff0c;并使用预定义的语义模块编写业务决策…

Drools 入门例子

本人参照drools-4.0.7-examples修改&#xff0c;可以到这个网址去下载&#xff1a;http://www.jboss.org/drools/downloads.html 1:下载4.0版eclipse &#xff1a; eclipse-java-ganymede-win32.zip并解压到D:/eclipse-java-ganymede-win32不需安装 2:下载drools插件 http://ww…

Drools

Drools入门系列&#xff08;一&#xff09;HelloWorldDrools入门系列&#xff08;二&#xff09;HelloWorld详解之Sample.drlDrools入门系列&#xff08;三&#xff09;HelloWorld详解之kmodule.xmlDrools入门系列&#xff08;四&#xff09;HelloWorld详解之JUnit Test类Drool…

规则引擎 Drools

规则引擎 Drools 文章目录 规则引擎 Drools1. 问题引出2. 规则引擎概述2.1 什么是规则引擎2.2 使用规则引擎的优势2.3 规则引擎应用场景2.4 Drools介绍 3.Drools入门案例3.1 业务场景说明3.2 开发实现3.3 小结3.3.1 规则引擎构成3.3.2 相关概念说明3.3.3 规则引擎执行过程3.3.4…

drools学习

drools使用和设置 1.概述举个例子 2.结合maven引入1.引入maven2.创建kmoudule.xml文件3如有需要,安装drools的插件4编写drl规则文件5准备用于测试的po类6编写drools的java接口 3.基本规则 4.组合语法 1.概述 drools是一个规则引擎,什么是规则引擎?就是能够自动将决策树转化为…

drools详解

1、基本的概念 请参考这个链接&#xff0c;差不多的语法感觉都介绍了 drools-api的基本语法链接 2、如何将drl文件配置在数据库中&#xff0c;实现动态加载&#xff1a; package com.neo.drools;import com.neo.drools.model.Message; import org.kie.api.io.ResourceType; i…

Drools基本介绍,入门案例,基本语法

目录 经典需求与场景 需求 传统做法-if 传统做法-策略 问题&#xff1f; 规则引擎 概念 起源 原理--基于 rete 算法的规则引擎 规则引擎应用场景 Drools 介绍 消费赠送积分案例 第一步&#xff1a; 创建工程&#xff0c;引入jar 创建 drools 自动配置类 订单实体…

Drools 简介

序 现实生活中&#xff0c;规则无处不在。对于某些企业级应用&#xff0c;诸如欺诈检测软件&#xff0c;购物车&#xff0c;活动监视器&#xff0c;信用和保密应用之类的系统&#xff0c;经常会有大量的、错综复杂的业务规则配置&#xff0c;而且随着企业管理者的决策变化&…

drools 介绍

1 .场景 1.1需求 商城系统消费赠送积分 100元以下, 不加分 100元-500元 加100分 500元-1000元 加500分 1000元 以上 加1000分 ...... 1.2传统做法 1.2.1 if...else if (order.getAmout() < 100){ order.setScore(0); addScore(order); }else if(order.getAmo…

计组——定点数原码反码补码移码以及它们之间的转换

原码 用尾数表示真值的绝对值&#xff0c;符号位“0/1”对应“正/负” 若机器字长n1位&#xff0c;原码整数的表示范围&#xff1a; − ( 2 n − 1 ) ≤ x ≤ 2 n − 1 {\color{Red} -(2^{n}-1)\leq x\leq 2^{n}-1} −(2n−1)≤x≤2n−1&#xff08;关于原点对称&#xff09;…

数据的表示:原码、反码、补码、移码以及浮点数的运算

前言 复习到数据表示方面相关的知识&#xff0c;所以在这里做一下记录&#xff0c;也方便大家参考。 什么是 R 进制 对于 R 机制&#xff0c;如果要实现与十进制的转换&#xff0c;则使用 按权展开法&#xff0c;其具体操作为&#xff1a; 将 R 进制数的每一位数值用 R k R…

五分钟理解原码补码反码和移码

这是计算机的基本知识了&#xff0c;一定要好好学。哈哈废话不多说&#xff0c;直接进入正题吧。计算机中有无符号数和有符号数两大类。 有符号数就是正负数&#xff0c;在计算机中正好用0和1分别去代表正和负。(ps:好多人不理解机器数和真值&#xff0c;机器数就是把符号数字…

原码、反码、补码、移码的表示

若字长n为8时&#xff0c;那么45的二进制表示0 0101101 &#xff0c;若数值X 1.原码 [X]原&#xff0c;在二进制数值中&#xff0c;正数保持不变&#xff0c;负数符号位置1. 2.反码 [X]反&#xff0c;的正数保持不变 &#xff0c; 负数对数值的绝对值每一位按位求反 3.补码…

关于补码移码各自和原码的联系、来历、功能及I EEE754标准中移码范围问题

最近在学习计算机组成原理时,遇到一些问题,记录在此。 如果你对下面这段话有疑惑或者兴趣&#xff0c;我或许能说点什么你感兴趣的。 真值-128的补码&#xff1a;1000 0000&#xff0c;这个补码本身表示的二进制数&#xff08;无符号&#xff09;是128&#xff0c;其对应着真…

移码的计算方式

规则&#xff1a;对应真值的补码的符号位取反&#xff1b; 计算公式&#xff1a; 式中&#xff1a;x为真值&#xff0c;n为整数的位数&#xff1b; 形式上补码是先减后增的&#xff0c;移码是递增的&#xff1b;根据人类的习惯&#xff0c;移码可以清楚的反映对应真值的大小…

计算机组成原理学习笔记:定点数、浮点数、原码、反码、补码、移码

定点数与浮点数 所谓定点数就是指小数点的位置固定不变而浮点数是小数点的位置是不固定的&#xff0c;会浮动 1 ) 定点数 用熟悉的十进制数来类比&#xff0c;定点数就是我们平时更习惯使用的常规的计数方式&#xff0c;我们会显式的标明小数点的位置Eg: 110.12 2 &#xf…

原码,补码,移码

一、原码 ①最高位为符号位&#xff0c;0表示正数&#xff0c;1表示负数&#xff1b; ②除符号位其它数值部分&#xff0c;就是数值本身绝对值的二进制数&#xff1b; ③负数的原码是在其绝对值得的基础上&#xff0c;符号位变为1&#xff1b; 但是&#xff1a; 0的表示不唯一&…

移码补码原理

计算机中的“数”&#xff0c;花样很多&#xff0c;又是ASCII码、又是BCD码等等&#xff0c;下面&#xff0c;做而论道写了一些关于移码、补码的一些看法&#xff0c;欢迎拍砖。 机器数 计算机中的“数”&#xff0c;其实都不是数字&#xff0c;它们都是一些高、低电平。其中&a…

浮点数与移码

浮点数的组成和计数原理 浮点数是什么浮点数的表示与规定浮点数的规定&#xff08;IEEE754 标准&#xff09;浮点数的表示范围&#xff08;IEE7标准瞎&#xff09;阶码用移码表示 浮点数是什么 浮点数就是小数点可以任意浮动的数字。   因为在计算机的机器语言中&#xff0c;…