Apache KIE 10 Drools 规则引擎实战详解
基于 Ubuntu 22.04 与 OpenJDK 17 的 Drools 实战详解,覆盖 DRL 规则编写、类路径与 KieHelper 两条触发路线、元数据诊断、激活取消机制与规则回归测试。
Apache KIE 10 Drools 规则引擎实战详解
一、实战目标与工程准备
本文在 Ubuntu 22.04 服务器、OpenJDK 17.0.20.1 与 Maven 3.6.3 上,围绕 org.kie 与 org.drools 的 10.2.0 五坐标(kie-api、drools-compiler、drools-core、drools-xml-support、drools-mvel),把 Drools 规则引擎从"第一条规则"走到"可回归的测试资产"。全部代码与输出来自同一个 Maven 工程(com.example:kie-demo:1.0.0)的真实执行:一条定价规则链(未成年人提示、成人基础折扣、VIP 叠加折扣、高价订单审计)经类路径模式触发五次;一条黑名单规则经 KieHelper 动态注入触发一次;引擎元数据诊断列出 KieBase 与 KieSession;最后把整条规则链固化成 JUnit 回归测试并做了失败对照实验。文中所有输出逐字实录,四个踩坑点附完整报错原文。
工程结构先行(三层资源各就各位):
/opt/kie-demo
├── pom.xml
└── src/main
├── java/com/example
│ ├── Person.java
│ ├── RuleApp.java
│ ├── HelperApp.java
│ └── DiagApp.java
└── resources
├── META-INF/kmodule.xml
└── rules/pricing.drl
集成步骤与依赖细节见本专栏的集成指南篇,本文聚焦规则本身:怎么写、怎么跑、引擎怎么调度、怎么把行为钉进测试。
四个实验的递进关系先交代清楚,它们不是并列关系而是一条认知阶梯:第一步 RuleApp 跑通"声明式规则被引擎调度"的完整闭环,建立对触发与排序的直觉;第二步 HelperApp 换到动态路线,理解知识库的隔离语义与规则校验的安全阀;第三步 DiagApp 退后一步做诊断,学会从元数据层看 KieContainer 里装配了什么——前两步是"用",第三步是"修";第四步把前三步钉进 JUnit 回归,用一次真实失败与一次真实修复证明"规则行为可以被测试资产固化"。四个实验共享同一套事实探针(张伟、李娜、王芳)与同一份 kmodule 配置,任何一环出错都会在下游的断言或输出里显形,这让四个实验天然互为校验。
二、被测资产:领域模型与规则文件
2.1 事实类 Person
package com.example;
public class Person {
private String name;
private int age;
private boolean vip;
private double price;
public Person(String name, int age, boolean vip, double price) {
this.name = name;
this.age = age;
this.vip = vip;
this.price = price;
}
public String getName() { return name; }
public int getAge() { return age; }
public boolean isVip() { return vip; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
}
事实类的设计约束只有一条硬规则:约束字段要有标准 getter(布尔字段走 isXxx 约定)。价格开放 setPrice 是因为两条折扣规则要在动作里修改它;其余字段只读,规则动作无法越权改写判断依据——把可变面收窄到业务确实需要变化的部分,是事实类设计的第一原则。
2.2 kmodule.xml
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="pricingKBase" packages="rules">
<ksession name="pricingKS" type="stateful"/>
</kbase>
</kmodule>
2.3 定价规则全文
src/main/resources/rules/pricing.drl 四条规则:
package rules
import com.example.Person
rule "未成年人提示"
salience 10
when
$p : Person(age < 18)
then
System.out.println("[未成年人提示] " + $p.getName() + " 年龄 " + $p.getAge() + ",需家长陪同");
end
rule "成人基础折扣"
when
$p : Person(age >= 18, price > 1000)
then
$p.setPrice($p.getPrice() * 0.9);
System.out.println("[成人基础折扣] " + $p.getName() + " 打9折,价格调整为 " + $p.getPrice());
end
rule "VIP 叠加折扣"
when
$p : Person(vip == true)
then
$p.setPrice($p.getPrice() * 0.95);
System.out.println("[VIP叠加折扣] " + $p.getName() + " 再打95折,价格调整为 " + $p.getPrice());
end
rule "高价订单审计"
when
$p : Person(price > 2000)
then
System.out.println("[高价订单审计] " + $p.getName() + " 当前价格 " + $p.getPrice() + " 超过 2000,进入人工审核");
end
四条规则各演示一个要素:salience 优先级(提示类规则先于一切折扣)、多条件与组合约束(age >= 18 且 price > 1000)、动作修改事实状态(两条折扣层层降价)、跨规则联动(审计消费折扣后的价格)。package 声明是小写的 rules,与 kmodule 的 packages 过滤严格对应——集成期曾把它写成 com.example.rules,结果四条规则被 kbase 静默丢弃(不报错、不触发、不警告),全靠 fireAllRules 返回 0 才暴露,这个坑在第七节实录展开。
三、主路线实测:类路径模式
3.1 主程序全文
package com.example;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
public class RuleApp {
public static void main(String[] args) {
KieServices ks = KieServices.Factory.get();
KieContainer kc = ks.getKieClasspathContainer();
System.out.println("KieContainer 构建完成,开始创建 KieSession...");
KieSession session = kc.newKieSession("pricingKS");
Person alice = new Person("张伟", 35, true, 2600.0);
Person bob = new Person("李娜", 16, false, 500.0);
Person carol = new Person("王芳", 28, false, 1500.0);
session.insert(alice);
session.insert(bob);
session.insert(carol);
int fired = session.fireAllRules();
System.out.println("共触发规则数: " + fired);
System.out.println("最终价格 -> 张伟: " + alice.getPrice()
+ " 李娜: " + bob.getPrice()
+ " 王芳: " + carol.getPrice());
session.dispose();
}
}
3.2 运行输出与逐行解读
MAVEN_OPTS=-Dfile.encoding=UTF-8 mvn -q exec:java -Dexec.mainClass=com.example.RuleApp
实测输出(逐字):
KieContainer 构建完成,开始创建 KieSession...
[未成年人提示] 李娜 年龄 16,需家长陪同
[成人基础折扣] 张伟 打9折,价格调整为 2340.0
[成人基础折扣] 王芳 打9折,价格调整为 1350.0
[VIP叠加折扣] 张伟 再打95折,价格调整为 2223.0
[高价订单审计] 张伟 当前价格 2223.0 超过 2000,进入人工审核
共触发规则数: 5
最终价格 -> 张伟: 2223.0 李娜: 500.0 王芳: 1350.0
逐行解读这份输出是理解引擎调度最好的教材。第一,触发顺序与插入顺序无关:李娜的提示最先打印,因为 salience 10 把它排在议程最前;三条无优先级规则按引擎自身的调度序展开。第二,价格链的演进:张伟 2600 → 2340(成人基础折扣)→ 2223(VIP 叠加),每次修改都通过 setter 发生,引擎随之重新评估相关约束。第三,审计规则只在 2223 时点打印了一次——尽管张伟的价格在 2600、2340 两个时点同样满足 price > 2000。这第四点是 Drools 最精妙也最容易误解的行为:动作修改事实会取消该事实此前挂起的同名激活并按新状态重建(防止同规则对同一事实重复触发),所以审计规则"跟到了最终状态"而不是"每个中间状态都响一次"。想要每个价格变动都留痕,正确做法是在折扣规则的动作里 insert 一条独立的审计事实,而不是指望原规则反复触发。
还有一个值得停一下的观察:成年折扣对张伟与王芳各触发了一次,两条同名打印行分别对应两个不同的事实对象——模式匹配按事实逐个评估,同一规则对同一批次里不同事实各自形成独立激活。这与命令式思维里"一个 if 分支走一次"的直觉不同:规则是"对每个满足者执行一次",批量场景下它的执行次数是数据驱动的。写规则动作时想清楚"这条规则可能对多少个事实各执行一次",是避免副作用放大(比如在动作里发通知)的第一课。
3.3 与手写 if-else 的对账
主程序最后的"最终价格"行用取值器直接读回事实状态:张伟 2223.0(2600 × 0.9 × 0.95)、李娜 500.0(无规则命中)、王芳 1350.0(1500 × 0.9)。它与纯 Java 公式 2600 * 0.9 * 0.95 = 2223.0 完全吻合——用独立路径的算术对账引擎输出,是规则工程最低成本的正确性验证。这一步在心理上也很重要:确认"引擎只是在调度声明式规则"而不是"黑盒里发生了别的事",之后才谈得上放心扩展规则集。
四、动态路线实测:KieHelper 编程式规则
4.1 HelperApp 全文
package com.example;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.utils.KieHelper;
public class HelperApp {
public static void main(String[] args) {
String drl = ""
+ "package rules;\n"
+ "import com.example.Person;\n"
+ "rule \"黑名单拦截\"\n"
+ " when\n"
+ " $p : Person(name == \"老赖\")\n"
+ " then\n"
+ " System.out.println(\"[黑名单拦截] \" + $p.getName() + \" 拒绝下单\");\n"
+ "end\n";
KieHelper helper = new KieHelper();
helper.addContent(drl, ResourceType.DRL);
Results results = helper.verify();
if (results.hasMessages(Message.Level.ERROR)) {
System.out.println("规则编译失败: " + results.getMessages());
return;
}
KieSession session = helper.build().newKieSession();
session.insert(new Person("老赖", 40, false, 9999.0));
session.insert(new Person("张伟", 35, true, 2600.0));
int fired = session.fireAllRules();
System.out.println("共触发规则数: " + fired);
session.dispose();
}
}
4.2 运行输出与要点
[黑名单拦截] 老赖 拒绝下单
共触发规则数: 1
两个事实进去只有一条规则被触发:张伟虽然满足定价规则链的全部条件,但这个会话里只注入了黑名单一条规则——KieHelper 构建的 KieBase 与 classpath 上的 pricing.drl 完全隔离。这正是动态路线的语义边界:它构建的是"这次调用自己的知识库",与类路径模式互不串扰。verify 先行是这条路线的安全阀——坏规则(语法错误、引用不存在的字段)在 build 之前就被拦截进 Results,生产系统把 verify 的消息列表接进审核界面,就形成了"规则上线前人工把关"的闭环。addContent 的第二个参数 ResourceType.DRL 声明内容类型,同样的入口还支持 BPMN2、XLS 决策表等资源类型。
黑名单规则的条件写的是 name == "老赖"——按姓名拦截在真实系统里是反模式(重名、改名都会误伤),这里用作演示是因为它把"约束即等值比较"这一最简形态展示得最清楚。真实的黑名单应挂在独立的信用事实或编号字段上。这个替换练习留给读者:把 when 改成 $p : Person(name memberOf $blacklist) 并从外部 insert 一个黑名单集合,就得到了可以运营维护的拦截规则——规则的骨架不变,约束表达力随业务需要升级,这正是声明式规则的扩展路径。
五、引擎元数据诊断
第三个小程序 DiagApp 演示"从外面看 KieContainer 里有什么":
package com.example;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
public class DiagApp {
public static void main(String[] args) {
KieServices ks = KieServices.Factory.get();
KieContainer kc = ks.getKieClasspathContainer();
System.out.println("KieBase names: " + kc.getKieBaseNames());
System.out.println("Session names: " + kc.getKieSessionNamesInKieBase("pricingKBase"));
}
}
实测输出:
KieBase names: [pricingKBase]
Session names: [pricingKS]
只有两行输出,诊断价值却不小。newKieSession 返回 null(集成指南篇实录的故障)时,第一步就该跑这两行:KieBase names 为空说明 kmodule.xml 没被解析(缺 drools-xml-support 或文件位置错误);KieBase 在但 Session names 不含目标名字,说明 ksession 拼写或归属错误;两者都正常而会话仍为 null,才轮到怀疑依赖缺失之外的深层问题。把这两行做成集成自检的第一站,能把"会话为 null"这类问题的排查时间从小时级压到分钟级——诊断先于修复,顺序不能反。
再往深一层,getKieBaseNames 返回的是名单而不是状态:它能告诉你"装配层认为有哪些 kbase",但不能告诉你 kbase 里有没有规则(package 不匹配的静默丢弃发生在 kbase 收编资源的更深处,名单不受影响)。所以诊断的完整链路是三段:元数据名单(DiagApp 这两行)→ 会话可达性(newKieSession 非 null)→ 行为验证(fireAllRules 计数与探针事实的预期一致)。三段各自对应一类故障——装配缺失、配置拼写、资源收编——按序排查就不会在错误的层级里翻找。本文四条规则的工程里,第一段输出 [pricingKBase]、第二段 [pricingKS]、第三段计数 5,三个信号全部正常时才有资格说"规则链是通的"。
六、规则行为深挖
6.1 议程与调度:salience 只是冰山一角
fireAllRules 一次性触发所有已激活的规则,执行顺序由议程决定。salience 是最直接的排序键,但 10.x 的完整调度还涉及规则的声明顺序、事实的插入时序(越新的事实激活越靠前)与规则组(agenda-group)的控制流。本文实测刻意只用了 salience 一个维度,让"优先级排序"这一最常用手段先建立直觉;agenda-group 与激活组属于流程式控制的进阶话题,入门阶段一律不用反而是最佳实践——顺序控制用得越多,规则集的不可预测性越强,这与声明式的初衷背道而驰。
6.2 动作修改状态的两面性
规则动作通过 setter 修改事实,是引擎联动的基础(折扣后触发审计),也是无限循环的温床(修改又重新满足自身条件时反复触发)。Drools 的防线有三层:属性反应性(默认只在与该字段相关的约束网络上重新评估,实测的审计规则只在 price 相关网络上被重建)、激活取消机制(3.2 节实录的"旧激活作废")、以及 no-loop 等显式声明。本文的四条规则靠乘法交换性与激活取消机制自然收敛,没有引入任何循环控制属性——这给出了一个设计判据:先让规则集"天然收敛"(动作单调、条件有界),循环控制属性只作为防御性兜底,不要指望 no-loop 去拯救逻辑上就会震荡的规则集。
"动作单调"值得再解释一层:折扣规则的每次修改都让价格严格下降(乘以小于 1 的系数),而审计规则只读不写——写入方向单向、读取方无副作用,这样的系统在数学上保证收敛。反例是两条规则互相抬价(A 规则给 VIP 加价、B 规则看到高价再补贴),写入方向交错且阈值交叉,议程里的激活会以振荡的形式永不停歇,fireAllRules 永不返回。设计规则集时先画一张"谁写哪些字段、谁读哪些字段"的依赖图,写读交错处就是循环风险点——这张图五分钟就能画完,却能让团队避开规则引擎里最难缠的排障类别。
6.3 顺序无关的规则设计
两条折扣规则的乘法交换性让最终价格与调度顺序无关,这是本文规则集能稳定通过回归的根本原因。对照反例:如果成人基础折扣是"满 2000 减 300"而 VIP 叠加是"总价再打 95 折",两个动作的先后就会改变结果(满减阈值对价格敏感),规则集就从"顺序无关"退化为"顺序敏感"。顺序敏感的规则集不是不能写,而是必须显式用 salience 锁定顺序并写进规则注释——把它变成设计决策而不是调度偶然。这条原则与分布式系统的"操作可交换"是同源智慧,规则引擎只是把它放大到了更肉眼可见的程度。
七、排障实录
7.1 package 不匹配:规则被静默丢弃
DRL 的 package 写成 com.example.rules 而 kmodule 的 packages 是 rules 时,mvn compile 照常通过、KieContainer 照常构建、newKieSession 照常返回非 null——但 fireAllRules 返回 0,四条规则一条不触发。引擎对"没被任何 kbase 收编的规则"的态度是完全沉默。定位手段:跑 DiagApp 确认 KieBase 存在后,检查 DRL 首行 package 声明与 kmodule packages 的字符串匹配(它按 DRL 内部声明匹配,与资源文件路径无关)。规则包名永远用小写单词、与 kmodule 同步维护,是本实录沉淀的规范。
7.2 缺 drools-xml-support:会话静默为 null
依赖摘除后编译通过,运行在 session.insert 处抛出:
Cannot invoke "org.kie.api.runtime.KieSession.insert(Object)" because "session" is null
KieContainer 构建不报错、newKieSession 返回 null、错误在第一次使用会话时才爆炸——错误现场与根因隔了两层。先跑 DiagApp(KieBase names 为空即确认装配层失效),再查 pom 是否带 drools-xml-support。修复后五次触发完整复现。
7.3 缺 drools-mvel:占位实现抛 UnsupportedOperationException
依赖摘除后运行报 message 为 null 的 UnsupportedOperationException,-e 展开堆栈:
Caused by: java.lang.UnsupportedOperationException
at org.drools.compiler.rule.builder.ConstraintBuilder$DummyConstraintBuilder.setExprInputs (ConstraintBuilder.java:210)
DummyConstraintBuilder 是约束编译器的占位实现,规则约束(age >= 18、vip == true)的编译全落在这条路径。指纹清晰:看到 DummyConstraintBuilder 即为缺 mvel,补依赖即愈。
7.4 编码:中文规则名乱码后撞成重名
默认平台编码(ANSI_X3.4-1968)下运行,UTF-8 的 DRL 被误读,中文规则名全部变成问号串,引擎报 Duplicate rule name:
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.1.0:java (default-cli) on project kie-demo: An exception occurred while executing the Java class. Error while creating KieBase[Message [id=1, kieBase=pricingKBase, level=ERROR, path=/opt/kie-demo/target/classes/rules/pricing.drl, line=13, column=0
[ERROR] text=Duplicate rule name: ??????????????????], Message [id=2, kieBase=pricingKBase, level=ERROR, path=/opt/kie-demo/target/classes/rules/pricing.drl, line=29, column=0
[ERROR] text=Duplicate rule name: ??????????????????]]
修复:exec:java 在 Maven 的 JVM 里跑,MAVEN_OPTS=-Dfile.encoding=UTF-8 直接生效。同一工程换成 surefire 跑 JUnit 时坑会换一个形态重现(见第八节实录)——fork 出去的测试 JVM 不继承 MAVEN_OPTS,必须用 argLine。规则名用 ASCII、说明性信息放 DRL 注释,是更彻底的防御。
八、把规则链固化成回归测试
8.1 测试代码与运行实录
在工程里临时加入 junit 4.13.2(test 范围)与如下测试类:
package com.example;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
import static org.junit.Assert.assertEquals;
public class PricingRuleTest {
@Test
public void testPricingChain() {
KieSession session = new org.kie.api.KieServices.Factory().get()
.getKieClasspathContainer().newKieSession("pricingKS");
try {
Person a = new Person("张伟", 35, true, 2600.0);
Person b = new Person("李娜", 16, false, 500.0);
Person c = new Person("王芳", 28, false, 1500.0);
session.insert(a);
session.insert(b);
session.insert(c);
int fired = session.fireAllRules();
assertEquals(5, fired);
assertEquals(2223.0, a.getPrice(), 0.001);
assertEquals(500.0, b.getPrice(), 0.001);
assertEquals(1350.0, c.getPrice(), 0.001);
} finally {
session.dispose();
}
}
}
断言三元组完整钉住规则链行为:触发计数(5)、三个人的最终价格(2223/500/1350)。首次 mvn test 直接复现了编码坑的 surefire 形态——Duplicate rule name 问号串(与集成指南篇 9.1 同源),用 argLine 修复:
mvn test -DargLine="-Dfile.encoding=UTF-8"
实测输出:
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.704 sec
8.2 失败对照:回归测试怎么"报错"
把期望触发数故意改成 4(制造一个"规则被人改坏"的场景),重跑:
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.583 sec <<< FAILURE!
java.lang.AssertionError: expected:<4> but was:<5>
清晰的 expected 与 was 对照,让"谁改坏了规则、改成了什么样"在持续集成面板上一目了然。这两个实测(绿与红各一次)共同验证:规则链可以被钉进 JUnit 资产,任何一次规则文件或引擎版本的变更都会在秒级回归里现形。对本工程而言测试用例完成后已按实验计划移除、pom 恢复原状,但测试代码本身(约二十行)可直接复制进任何真实工程复用。
这次实验还顺手验证了一个跨工具链的规律:同一条编码故障(中文规则名乱码成重名)在 exec:java 与 surefire 两个运行通道里以不同形态出现、需要不同的传参通道(MAVEN_OPTS 对前者有效、argLine 对后者有效)。把它写进团队的开发规范,新成员可以少绕一大圈:规则工程里凡是涉及"运行或测试",编码参数必须两处都配,缺一处就在对应通道复现一次故障——不是玄学,是两个 JVM 各有各的参数来源。
8.3 测试策略的三层结构
规则工程的测试金字塔:最底层是本文这样的"规则链行为钉"(毫秒级、进构建流水线);中层是"规则变异测试"(故意改坏一条规则断言计数下降,验证测试自身的灵敏度);顶层是少量端到端冒烟(真实部署形态下规则可触发)。三层共享同一套事实探针(张伟/李娜/王芳的组合),探针资产的生命周期比任何一条规则都长——规则会频繁变,探针代表的"业务不变量"很少变。给探针单独建一个测试工具类、把"构造探针 + 断言三元组"封装成可复用方法,是让这套资产长期服役的最后一块拼图。
九、常见问题(FAQ)
1. fireAllRules 返回 0 但没有报错,从哪查起? 三步:DiagApp 确认 KieBase 与 KieSession 名单;核对 DRL 的 package 与 kmodule 的 packages 是否字符串一致(静默丢弃的头号原因,本文 7.1 实录);确认事实确实 insert 且约束字段 getter 存在。
2. 为什么审计规则只触发了一次? 激活取消机制:动作修改事实后,该事实挂起的同名激活被取消并按新状态重建,规则最终只在最后一个满足状态上触发(本文 3.2 逐行解读)。要每次变动留痕,在动作里 insert 独立审计事实。
3. KieHelper 构建的会话和类路径会话会互相干扰吗? 不会。两条路线各自构建 KieBase, HelperApp 实测里只有注入的黑名单规则被触发(计数 1),classpath 上的定价规则完全不在场。混用时按业务域规划好各自的规则来源即可。
4. 动作里能用 return 之类控制流吗? DRL 的 then 部分是 Java 语句子集,支持声明变量与方法调用,但"跳过其他规则"这类控制流要用规则属性(agenda-group、activation-group)表达,而不是在动作里硬编码——动作控制单个规则的行为,属性控制规则间的关系。
5. salience 数字应该怎么规划? 小而留间隔(如 10、20、50),不要连续整数——为将来插入中间优先级留余量。同一 salience 内的顺序交给引擎(不依赖),真正需要顺序的规则才用 salience 区分,这是"顺序无关优先"设计的直接落地。
6. 测试断言里 price 用 0.001 的 delta 是必须的吗? double 运算有浮点误差,assertEquals(price, actual, delta) 的 delta 是浮点断言的标准姿势;本文 2600 × 0.9 × 0.95 在 double 下的表示恰好稳定,但换任意输入就未必。生产金额场景换 BigDecimal 并用 compareTo 断言,一并解决精度与相等语义。
7. 一个工程能放多个 DRL 文件吗? 可以,只要 package 命中 kmodule 的 packages 过滤就全部收编。文件是物理组织、包名是逻辑组织,大型规则集按子包分文件,kbase 不用改。
8. 规则修改后怎么让变更生效? 进程内不热加载:重新 mvn compile 后新起 JVM 生效。动态生效走 KieHelper 重建或 KJAR 版本化(集成指南篇第七节有三路线对比)。开发期反复改规则的节奏下,exec:java 一条命令就是完整循环。
9. verify 报了 ERROR 但规则语法看着没错? 常见原因是 import 缺失(类型不在默认包里)与字段名拼写(引擎按 getter 反射)。verify 的消息带 path 与 line 定位,逐条修完再 build,不要带病运行。
10. 引擎会重排我 insert 的事实顺序吗? 工作内存无序(这是事实,不是 bug),约束匹配的网络只认约束不认顺序。依赖插入顺序的规则设计本身就是反模式;需要顺序语义时把序号作为字段写进事实。
11. 10.x 里 KieHelper 的 org.kie.internal 前缀要担心吗? 它是"公开但弱兼容承诺"层。动态规则场景下它是官方提供的最短路径,锁定版本使用风险可控;纯静态规则的工程可以完全避开它(本文主路线全程未用)。
12. 这套实战的实验工程可以直接拿走吗? 可以。pom 五坐标 + 三个 Java 类 + kmodule + 一份 DRL 是自足的最小集,本文全部输出都出自它,任何 Ubuntu 22.04 + JDK 17 + Maven 3.6.x 的环境按集成指南篇装配即可复现相同结果。
十、参考链接
- Apache KIE(孵化中)官网:https://kie.apache.org/
- Maven Central 上的 drools-compiler 10.2.0:https://central.maven.org/artifact/org.drools/drools-compiler/10.2.0
- Maven Central 上的 drools-mvel 10.2.0:https://central.maven.org/artifact/org.drools/drools-mvel/10.2.0
- Drools 官方文档(历史主线):https://www.drools.org/
- JUnit 4 官方文档:https://junit.org/junit4/
- Apache Maven 官方文档:https://maven.apache.org/guides/
- OpenJDK 17 发布页:https://openjdk.org/projects/jdk/17/