1. 概述
在复杂业务系统中,将业务规则从核心代码中剥离,是提升可维护性和降低变更成本的有效手段。规则引擎正是为此而生——它让业务逻辑变得可配置、可热更新,避免每次改需求都要动代码、重新打包上线。
在之前的 Java 规则引擎综述 一文中,我们提到了 JSR 94 这一规范。而 Jess Rule Engine 正是该规范的参考实现(Reference Implementation),地位特殊,值得深入研究。
本文将带你从零开始,实战集成 Jess,并通过 JSR 94 标准化接口,实现规则引擎的“热插拔”。
2. Jess 规则引擎简介
Jess 是最早一批能与 Java 无缝集成的规则引擎之一。其核心基于高效 Rete 算法的增强实现,在多数场景下,性能远超简单的 Java 循环判断。
✅ 核心特点:
- 原生规则语言:使用类 Lisp 的语法编写
.clp
文件,简洁强大。 - 支持 XML:也可用 XML 格式定义规则(更啰嗦,本文不采用)。
- REPL 交互式环境:提供命令行界面,可快速验证规则逻辑,踩坑利器。
- Eclipse 插件:有配套的开发工具(兼容旧版 Eclipse)。
- JSR 94 兼容:作为参考实现,天然支持标准 API。
⚠️ 注意:Jess 本身是商业闭源产品,虽提供免费试用,但生产环境需授权。其 JSR 94 驱动层是开源的。
2.1. JSR 94 规范简述
JSR 94 的核心价值在于解耦。它定义了一套标准 API,让你的 Java 代码无需关心底层是 Jess、Drools 还是其他引擎。
这意味着:
- ✅ 切换引擎只需改配置:替换 Maven 依赖和驱动类名即可。
- ❌ 规则文件仍需重写:不同引擎的规则语法差异巨大,无法直接复用。
简单粗暴地说,JSR 94 让你的应用代码与规则引擎实现解耦,但规则逻辑本身还是绑定在具体引擎上的。
2.2. Jess 的 JSR 94 驱动
JSR 94 规范包中自带一个 org.jcp.jsr94.jess
包下的参考驱动,但不包含 Jess 引擎本体。
推荐使用官方下载包中提供的更新、更稳定的驱动,位于 jess.jsr94
包下。集成时,需同时引入 jess.jar
(引擎) 和 JSR 94 API。
我们先看原生集成,再对比 JSR 94 的用法差异。
3. 示例运行验证
动手前,先确保环境就绪。你需要:
- 注册并下载 Jess 7.1p2 版本(含 30 天试用)。
- 解压得到
Jess71p2.jar
,并将其加入项目 classpath。
3.1. 运行原生 Jess 示例
进入解压目录的示例文件夹,运行定价引擎 demo:
cd Jess71p2/examples/pricing_engine
ant test
预期输出:
Buildfile: Jess71p2\examples\pricing_engine\build.xml
...
test:
[java] Items for order 123:
[java] 1 CD Writer: 199.99
...
[java] Items for order 666:
[java] 1 Incredibles DVD: 29.99
[java] Offers for order 666:
[java] BUILD SUCCESSFUL
Total time: 1 second
输出成功,说明 Jess 运行环境正常。
3.2. 运行 JSR 94 示例
下载 JSR 94 规范包:
unzip jreng-1_0a-fr-spec-api.zip
得到
jsr94-1.0
目录。关键一步:将之前下载的
Jess71p2/lib/jess.jar
复制到jsr94-1.0/lib/
目录下。否则会报错:Error: The reference implementation Jess could not be found.
运行示例:
java -jar jsr94-1.0/lib/jsr94-example.jar
预期输出:
Administration API Acquired RuleAdministrator: org.jcp.jsr94.jess.RuleAdministratorImpl@63947c6b
...
Runtime API Acquired RuleRuntime: org.jcp.jsr94.jess.RuleRuntimeImpl@68fb2c38
Customer credit limit result: 3000
...
Invoice 2 amount: 1750 status: paid
Released Stateful Rule Session.
成功!证明 JSR 94 与 Jess 驱动协同工作正常。
4. 原生方式集成 Jess
现在,我们用 Java 代码直接调用 Jess API 来执行规则。
4.1. Maven 依赖
由于 Jess 未发布到中央仓库,需手动安装到本地:
mvn install:install-file -Dfile=jess.jar -DgroupId=gov.sandia -DartifactId=jess -Dversion=7.1p2 -Dpackaging=jar -DgeneratePom=true
然后在 pom.xml
中添加:
<dependency>
<groupId>gov.sandia</groupId>
<artifactId>jess</artifactId>
<version>7.1p2</version>
</dependency>
4.2. 第一个规则文件
创建最简单的规则文件 hellojess.clp
:
(printout t "Hello from Jess!" crlf)
4.3. 执行规则
Java 代码创建引擎实例并执行:
public class HelloJess {
public static void main(String[] args) throws JessException {
Rete engine = new Rete();
engine.reset(); // 重置到初始状态
engine.batch("hellojess.clp"); // 加载规则文件
engine.run(); // 执行规则
}
}
运行输出:
Hello from Jess!
5. 原生集成:数据交互
规则引擎的价值在于处理数据。下面我们看如何传入 Java 对象,让规则处理,并获取结果。
5.1. 数据模型
定义 Question
和 Answer
类:
public class Question {
private String question;
private int balance;
// getters and setters
public Question(String question, int balance) {
this.question = question;
this.balance = balance;
}
}
public class Answer {
private String answer;
private int newBalance;
// getters and setters
public Answer(String answer, int newBalance) {
this.answer = answer;
this.newBalance = newBalance;
}
}
5.2. 带数据处理的规则
创建 bonus.clp
规则文件:
(import com.baeldung.rules.jsr94.jess.model.*)
(deftemplate Question (declare (from-class Question)))
(deftemplate Answer (declare (from-class Answer)))
(defrule avoid-overdraft "Give $50 to anyone overdrawn"
?q <- (Question { balance < 0 })
=>
(add (new Answer "Overdrawn bonus" (+ ?q.balance 50))))
代码解析:
(import ...)
和(deftemplate ...)
声明了 Java 类,使其可在规则中使用。?q <- (Question { balance < 0 })
是条件(LHS),?q
是绑定变量,匹配所有余额小于 0 的Question
对象。=>
后是动作(RHS),使用(add ...)
将新创建的Answer
对象放入工作内存。
5.3. Java 代码操作数据
Rete engine = new Rete();
engine.reset();
// 加载规则
engine.batch("bonus.clp");
// 创建数据并加入工作内存
Question question = new Question("Can I have a bonus?", -5);
engine.add("Question", question); // 或使用 engine.add() 直接添加对象
// 执行规则
engine.run();
// 从工作内存中提取结果
Iterator results = engine.getObjects(new jess.Filter.ByClass(Answer.class));
while (results.hasNext()) {
Answer answer = (Answer) results.next();
System.out.println("Answer: " + answer.getAnswer() + ", New Balance: " + answer.getNewBalance());
}
// 高效复用:使用标记(Marker)重置状态
WorkingMemoryMarker marker = engine.mark(); // 在加载完参考数据后打标记
// ... 处理本次请求数据 ...
engine.resetToMark(marker); // 重置,准备处理下一批数据
6. 使用 JSR 94 集成 Jess
现在,我们用标准化的 JSR 94 API 重写上述逻辑,实现引擎无关性。
6.1. Maven 依赖
<dependency>
<groupId>jsr94</groupId>
<artifactId>jsr94</artifactId>
<version>1.1</version>
</dependency>
6.2. 管理 API (javax.rules.admin)
用于加载和注册规则集:
// 1. 获取服务提供者
String RULE_SERVICE_PROVIDER = "jess.jsr94";
Class.forName(RULE_SERVICE_PROVIDER + ".RuleServiceProviderImpl");
RuleServiceProvider ruleServiceProvider = RuleServiceProviderManager.getRuleServiceProvider(RULE_SERVICE_PROVIDER);
// 2. 获取管理员并注册规则集
RuleAdministrator ruleAdministrator = ruleServiceProvider.getRuleAdministrator();
InputStream ruleInput = getClass().getResourceAsStream("/bonus.clp");
HashMap vendorProperties = new HashMap(); // 供应商特定属性,Jess 可忽略
RuleExecutionSet ruleExecutionSet = ruleAdministrator
.getLocalRuleExecutionSetProvider(vendorProperties)
.createRuleExecutionSet(ruleInput, vendorProperties);
String rulesURI = "rules://com/baeldung/rules/bonus";
ruleAdministrator.registerRuleExecutionSet(rulesURI, ruleExecutionSet, vendorProperties);
6.3. 执行 API (javax.rules)
用于创建会话、传入数据并获取结果:
// 3. 获取运行时并创建无状态会话
RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime();
StatelessRuleSession statelessRuleSession = (StatelessRuleSession)
ruleRuntime.createRuleSession(rulesURI, new HashMap(), RuleRuntime.STATELESS_SESSION_TYPE);
try {
// 4. 执行规则并获取结果
List<Object> data = new ArrayList<>();
data.add(new Question("Can I have a bonus?", -5));
List<Object> results = statelessRuleSession.executeRules(data);
// 5. 处理结果 (JSR 94 未使用泛型)
for (Object obj : results) {
if (obj instanceof Answer) {
Answer answer = (Answer) obj;
System.out.println("Answer: " + answer.getAnswer() + ", New Balance: " + answer.getNewBalance());
}
}
} finally {
statelessRuleSession.release(); // 释放资源
}
✅ JSR 94 优势总结:
- 标准化:代码与具体引擎解耦。
- 可替换:理论上可无缝切换到其他 JSR 94 兼容引擎(如早期的 Drools)。
- 清晰分层:
admin
包负责规则管理,runtime
包负责规则执行。
⚠️ 缺点:
- API 较老(JDK 1.4 时代),不支持泛型,代码稍显啰嗦。
- 实际生态中,Drools 等主流引擎已转向自己的 API,JSR 94 应用较少。
7. 结论
本文详细演示了如何通过原生 API和JSR 94 标准两种方式集成 Jess 规则引擎。
- 原生方式:简单直接,性能最优,适合深度绑定 Jess 的项目。
- JSR 94 方式:牺牲一点便捷性,换取未来可能的引擎迁移灵活性。
尽管 Jess 作为商业产品和 JSR 94 规范的活跃度已不如从前,但其设计思想和 Rete 算法实现,仍是理解规则引擎原理的绝佳案例。
延伸阅读:
- Jess 官方文档:Embedding Jess in a Java Application
- Oracle JSR 94 指南:Getting Started With the Java Rule Engine API (JSR 94)
本文所有代码示例已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/rule-engines-modules/jess