上周在技术群里又有人抛老问题:Java 规则引擎哪个好?下面清一色在聊 Drools、Easy Rules、RuleBook。我看了半天回了一句:如果你们公司还跑着 WebSphere ILOG JRules 的老系统,这个问题就得换个问法——怎么把这套老牌规则引擎里的规则项目重新跑起来。从 JRules 7.1 一路用到 8.x,再到看它改名叫 IBM ODM,我这些年接手、重构过不少规则项目,深深觉得这类存量系统的难点从来不是规则写不出来,而是第一步就卡住:很多人连“新建一个规则项目”都建不对。
这篇是规则引擎连载的第三篇,前两篇聊了规则引擎能解决什么问题、整体架构大概长什么样,这篇就直接落到实操,讲清楚在 JRules 里新建一个规则项目的完整链路:项目结构、创建步骤、BOM/XOM 这些绕不开的概念、以及第一次跑通规则时最容易踩的坑。适合两类人看:一类是刚拿到老项目、必须在 Rule Studio 里改规则的开发者,另一类是还在评估要不要选 JRules 这类重型规则引擎框架、想先看看它开发手感的人。
1. 先搞清楚:JRules 的“规则项目”和普通 Java 项目差在哪
1.1 规则项目的资产结构:XOM、BOM、词汇表、规则集
很多第一次接触 JRules 的人最大的误解,是以为规则项目就是个 Java 项目,无非多几个规则文件。实际上,JRules 的 Rule Project 从设计上就不是为了让你写 Java 代码,而是为了管理“规则资产”而存在的。
普通 Java 项目里,代码是唯一的主角,src/main/java下面全是类和接口。但在 JRules 里,核心资产至少分成四层:
| 资产层 | 作用 | 谁主要维护 |
|---|---|---|
| XOM | 规则真正要调用的 Java 对象模型,可以是普通 POJO,也可以是从业务系统暴露出来的方法 | 开发人员 |
| BOM | 业务对象模型,把 XOM 里的类名、方法名、属性名包装成业务人员能看懂的概念 | 开发人员/规则架构师 |
| Vocabulary | 词汇表,把 BOM 里的概念继续加工成“至少”“不得”“享受折扣”这类业务短语 | 规则架构师/业务分析人员 |
| RuleSet | 规则集,存放具体规则,用接近自然语言的方式描述业务逻辑 | 规则开发者/业务人员 |
这句话得多说两遍:XOM 是给“机器执行”看的,BOM/Vocabulary 是给“业务理解”看的,RuleSet 才是业务逻辑真正待的地方。新建规则项目,本质上是把这四层资产的仓库先建起来,而不是简简单单建一个空目录。
1.2 用电商会员折扣举个例子
假设现在要做一条逻辑:黄金会员在下单时享受 8 折。
传统 Java 实现是在订单服务里写:
if ("GOLD".equals(customer.getGrade())) { order.setDiscount(new BigDecimal("0.8")); }业务要改成“黄金会员 75 折、白银会员 95 折”,你就得改代码、走发布流程。JRules 的思路是把这种条件判断从代码里拿出来,放到规则项目里,变成类似:
if the grade of the customer is "GOLD" then set the discount of the order to 0.8;要让这行规则能跑通,底层 XOM 里必须有Customer.getGrade()和Order.setDiscount();BOM 里必须把Customer说成“客户”、grade说成“会员等级”;Vocabulary 里还要把is对应到equals。这四个环节少了哪一个,规则要么编译不过,要么在规则编辑器里根本打不出来,业务人员看着就是一团乱码。
所以,新建规则项目这件事,重点不是“点几个下一步”,而是把你接下来的资产怎么组织、怎么命名、怎么映射,提前想明白。
2. 在 Rule Studio 里新建一个 Rule Project 的完整流程
2.1 环境准备
JRules 各版本的开发工具叫法不太一样,老版本通常叫 Rule Studio,后来的 IBM ODM 版本里叫 Rule Designer,下面统一叫它 Rule Studio。不管叫法怎么变,它本质上是 Eclipse 插件集,所以你第一步要装对环境。
我建议你开工前先确认四件事:
- Rule Studio 版本和你要连的 Rule Execution Server(RES)版本一致,至少大版本不能差太远。比如 8.x 的规则项目导出到 7.1 的 RES 就很容易失败。
- JDK 版本和 Rule Studio 要求的匹配。JRules 7.x 时代很多环境还是 JDK 1.5/1.6,后来 8.x 才逐步支持更高版本;你用高版本 JDK 打开老工作区,大概率会报
UnsupportedClassVersionError。 - 工作区编码统一。这一步最容易忽略,后面中文注释和规则文本全乱码就是在这里埋下的雷。
- 确认你是用“Rule Studio 的工作区”打开项目,而不是用普通 Eclipse 直接双击
.project文件。规则项目带专门的 Project Nature,普通 Eclipse 没有对应插件时会把它当成未知工程。
这些听起来都是小事,但我真见过有人在这上面耗掉一整天。老工具不像现代 IDE 装完就能自动识别,环境不匹配时,错误信息往往还很暧昧。
2.2 新建 Rule Project 的向导步骤
下面以我比较常用的 JRules 7.1/8.x 界面为例,不同版本按钮位置会有一点偏移,但流程是通用的。
- 打开 Rule Studio,选择
File -> New -> Other,在向导里搜索“Rule Project”。如果这个入口没有,确认你已经切到 Rule Studio 相关的 Perspective,通常是Window -> Perspective -> Open Perspective -> Other里选规则开发透视图。 - 输入项目名称。我习惯用
Rule_OrderDiscount这种命名,规则项目名称一般不带版本号,版本号放在 RuleApp 层管理。 - 如果公司有固定的工作区规范,设置项目存放路径;没有特殊要求就保持默认。
- 向导后面会问要不要把项目同时创建成“Java 项目”,在多数版本里规则项目本身就带 Java 能力,建议保留,因为 XOM 的 Java 类往往就放在这个项目里。
- 根据版本不同,可能还有一步是让你选目标 RuleApp 或者 Execution Server 类型。这一项不选也可以后面再配,但如果你明确知道要部署到哪套环境,最好在这里就选好,后续导出 RuleApp 时能少折腾。
- 点
Finish。
到这里,项目已经在资源管理器里出现了。注意,你建的如果是普通 Java Project,那这一步开始就错了,后面在规则编辑器里怎么看怎么别扭。新建规则项目最怕的就是“用 Java 工程的思维硬套”,所以向导里只要看到 Rule Project 相关选项,优先选它。
2.3 建完后的默认结构和 Rule Explorer
新建完成之后,你不会像 Maven 项目那样看到一个标准的src/main/java。不同版本生成的目录结构会有差异,但通常会包含类似下面这些内容:
MyRuleProject ├── .project ├── .classpath ├── configuration ├── rules │ └── ... ├── src │ └── ... └── .settings千万不要急着往里堆.java文件。你先打开 Rule Studio 里的规则透视图,注意它和 Eclipse 的Package Explorer不一样,它会有一个专门的规则资源视图,一般叫Rule Explorer或Rules Explorer,里面不是按包名一层层展开,而是按:
- Rule Project
- BOM
- Vocabulary
- RuleSet
- XOM
这样的资产维度展示。初次看到这个视图的人容易慌,因为找不到自己熟悉的 Java 包结构。我的建议是你必须适应它,因为后面所有新建 BOM、词汇表、规则集的操作入口都在这里,这才是 JRules 项目真正的“主目录”。
3. 项目建完先别急着写规则:把 XOM/BOM/Vocabulary 这一串关系搭好
3.1 先搭 XOM:规则引擎真正执行的那些类
XOM 是规则引擎的“执行底座”。JRules 本身不替你写业务对象的 getter/setter,它只知道调用 Java 方法。你先得把规则要操作的类准备好。
创建 XOM 有两种常见做法:
- 直接在规则项目里新建一个 Java 类,作为 XOM。
- 在另一个普通 Java 项目里维护业务模型,然后在规则项目里添加项目依赖。
我更推荐第二种,因为业务模型往往不只在规则项目里被使用。如果业务系统已经有一个customer-service项目,里面的Customer对象已经存在,你没必要在规则项目里复制一份。直接在项目属性 -> Java Build Path -> Projects里把那个 Java 项目加进来即可。
一个最简的 XOM 类大概长这样:
package com.example.order.model; import java.math.BigDecimal; public class Customer { private String grade; private BigDecimal discount; public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } public BigDecimal getDiscount() { return discount; } public void setDiscount(BigDecimal discount) { this.discount = discount; } }注意一点:规则引擎最终是通过反射或直接字节码调用来访问这些类的,方法名、属性名都会被 BOM/Vocabulary 引用。一旦发布到 RES 之后再改方法名,规则项目里凡是引用了旧名字的地方全部要跟着改。所以 XOM 的命名要像定协议一样谨慎。
3.2 从 XOM 到 BOM:让规则说人话
XOM 里是一个叫Customer.getGrade()的方法,业务不会这么说话。BOM 干的事,就是把Customer.grade包装成业务概念“客户等级”。
在 Rule Explorer 里选中你的 Rule Project,右键新建一个 BOM。BOM 创建后,你需要把 XOM 里的类映射过来。大多数版本的编辑器里有一个“添加映射”之类的入口,选择包、类,然后逐个映射属性和方法。比较常见的操作是:
- 把
Customer映射为“客户”。 - 把
grade映射为“客户等级”。 - 把
discount映射为“折扣”。 - 把
getGrade()映射为“客户等级值”。
这一步做完,你在规则编辑器里输入“客户”,IDE 才能联想出相关属性和方法。如果发现规则里输入什么联想都没有,别急着怀疑语法,先回 BOM 里检查映射有没有生效。
3.3 设置 Vocabulary:搞定“至少”“折扣为”这种业务词汇
BOM 解决了“对象叫什么”,Vocabulary 解决“运算关系怎么说”。
同一个>=,在规则里可能被业务人员说成“至少”“不低于”“超过或等于”等等。JRules 的 Vocabulary 就是把这些运算符、动词、介词绑定到具体的 Java 操作上。
在电商场景里,你可以建一组词汇:
| 业务说法 | 底层操作 |
|---|---|
| 至少 | >= |
| 不超过 | <= |
| 是 | equals |
| 设置为 | setter 方法 |
| 享受 | 某个方法返回true |
这一步对于项目能否交给业务人员维护非常关键。很多团队把前后端开发做得很好,却在 Vocabulary 上偷懒,结果规则写出来仍然满屏英文方法名,业务人员看都看不懂,规则引擎的“业务价值”就打了对折。
3.4 RuleApp 与规则集的部署路径设计
项目结构建好了,很多人就开始写规则,完全忘了还要考虑部署单元。JRules 的部署单位叫 RuleApp,一个 RuleApp 里可以包含一个或多个 RuleSet。新建项目阶段,你至少要先把命名规划定下来。
我的习惯是:
- Rule Project 对应一个业务域,比如“订单中心”。
- RuleSet 对应一个决策点,比如“订单折扣计算”。
- RuleApp 对应一个可发布的组件,比如
OrderDiscountRuleApp,版本号用1.0起步。
为什么要在新建阶段考虑这个?因为 RuleApp 一旦发布到 RES,后续更新规则是整体替换还是热更新,很多都取决于你的 RuleSet 拆分粒度。规则全堆在一个 RuleSet 里,发布起来是大炸弹,改一条会员规则可能要把整批折扣规则一起重新发布;拆得太碎,又会有一堆小 RuleApp 要管理。我的建议是先按决策点拆,一个决策点一个 RuleSet,后续发现性能问题再做合并。
3.5 文件编码与 JDK 级别:不然后面全是乱码和版本报错
这是新建项目阶段最不起眼但最要命的一项。老牌 Java 规则引擎的项目里,很多配置文件、规则文件都带编码信息。如果你在 Windows 上用默认 GBK 创建了规则项目,后来团队其他人用 UTF-8 打开,中文注释和规则字符串全会变成乱码。
我建议在新建完项目后立刻去项目属性 -> Resource -> Text file encoding里显式设置成 UTF-8,如果公司有统一编码规范,用规范里的值。同时把.settings目录下的配置文件提交到版本控制,这样别人检出后不会因为本地环境差异又变回乱码。
JDK 级别也在这里一起检查。Rule Studio 项目属性里会有 Java 编译器级别选项,尽量和 RES 运行环境的 JDK 保持一致。项目文件里存的是.class文件版本号,规则引擎在服务器上加载这些类时如果版本不匹配,报的错基本都是UnsupportedClassVersionError,排查起来浪费生命。
4. 用一条最简单的规则把“新建项目”这件事验证闭环
4.1 新建 RuleSet 并写一条规则
配置都做完以后,别急着写复杂业务,先用一条最简单的规则把链路跑通。新建项目这个动作本身没有验证标准,真正验证标准是:你的规则项目能编译、规则能执行、结果可预期。
在 Rule Explorer 里右键 Rule Project,新建一个 RuleSet,名字就叫OrderDiscountRuleSet。有些版本里也叫 Rule Package,本质一样,就是一个放规则的容器。
然后在 RuleSet 里新建一条规则,我这里用接近 BAL(商务动作语言)的写法示意,具体语法以你安装版本生成的模板为准:
rule "GoldCustomerDiscount" when { Customer( grade == "GOLD" ); } then { Customer.setDiscount( new BigDecimal("0.8") ); }如果你用了 BOM/Vocabulary,编辑器里也可以切到业务语言视图,看到的效果更像:
if the grade of the customer is "GOLD" then set the discount of the customer to 0.8;第一次写规则,不要追求复杂,关键是确认这条规则能被规则编译器解析、BOM 映射正确、字段名称没打错。如果这一步 IDE 有红叉,优先双击看错误定位,大部分都是“找不到属性”“方法签名不匹配”,回去检查 BOM/XOM 映射即可。
4.2 本地跑一次规则测试
写完之后,Rule Studio 一般支持直接在编辑器里运行规则做本地测试。你不需要先起一套 RES,在开发环境里有一个轻量的规则会话容器可以用来做单测。
我常用的验证方式是这样的:
- 先准备一组测试输入,比如一个
grade = "GOLD"的客户对象。 - 执行规则集,让它返回或修改客户对象。
- 检查输出结果,比如
discount是否变成0.8。
一个最简单的验证表格大概是:
| 场景 | 输入客户等级 | 预期折扣结果 |
|---|---|---|
| 黄金会员 | GOLD | 0.8 |
| 非黄金会员 | NORMAL | 不修改 |
实际跑的时候,你会发现新手最容易在“预期结果”上翻车:不是规则写错,而是对象状态本身没准备好。比如Customer的discount初始值如果是null,规则集不满足条件时不会覆盖它,结果读到null也是对的。所以本地测试时要把初始状态定义清楚,否则会误判规则执行异常。
4.3 导出 RuleApp 并部署到 Rule Execution Server
本地测试通过后,再走一次部署验证,这个新项目才算真正“活”了。
在 Rule Studio 里右键规则项目,选择导出相关菜单,生成 RuleApp 归档文件。导出时通常会让你确认:
- RuleApp 名称和版本。
- 要包含哪些 RuleSet。
- 是否要把 XOM 的依赖一起打包。
我建议把 XOM 一起打包,除非你的 RES 服务器上已经通过共享库的方式提前放好了模型类。第一次做项目时,打包缺失是部署失败最常见的原因。服务端报ClassNotFoundException,十有八九不是网络问题,而是 XOM 没进 RuleApp。
然后在 Rule Execution Server 的管理控制台里上传这个归档,激活后通过一个测试接口调用规则集,传入同样的测试数据,看返回是否和本地一致。到这一步,从“新建项目”到“能跑规则”的最小闭环就完成了。
5. 我这些年在新项目阶段踩过的四个坑
5.1 建了普通 Java Project 而不是 Rule Project
这个错我见过太多次。现象是项目里能看到 Java 类,但无法新建 BOM、Vocabulary、RuleSet,菜单里压根没有对应入口。根因就是创建工程时选成了普通 Java Project,规则项目的 Project Nature 没挂上。
解决方式不是硬着头皮把规则文件手工拖进去,应该在项目属性或者右键菜单里找“添加规则项目特性”之类的操作。不同版本叫法不同,但思路都是给已有项目追加 JRules 的 Nature。如果找不到,宁可重新建一个 Rule Project,也别在错误的项目类型里硬撑。
5.2 属性在 BOM 里不可见
规则编辑器里输入客户对象后,联想列表为空,是最让人恼火的情况。这通常不是规则项目坏了,而是 BOM 映射没到位。
排查顺序是:先看 XOM 类里是不是没有 getter/setter;再看 BOM 里有没有把需要用的属性加进去;最后看 Vocabulary 里是否绑定了对应词语。我当时排查过一个离谱问题:Java 类里字段叫discountRate,getter 写成了getDiscount(),BOM 里映射时按方法名自动找,结果怎么都找不到,最后是肉眼看到 getter 和字段命名不一致才解决的。这种问题 IDE 不会报错,只能靠耐心。
5.3 中文乱码
规则项目里的中文乱码,经常不是规则文件本身的问题,而是整个工作区默认编码不对。你在 A 电脑用 UTF-8 创建,在 B 电脑用 GBK 打开,目之所及全是方块。
我的经验是两件事必须同时做:一是项目级编码显式设成 UTF-8,二是在团队里约定.settings相关文件必须提交。只改第一项,同事检出后仍是各自本机默认编码;只做第二项,新项目第一次创建时还是会错。
5.4 团队协作时 RuleApp 导出不一致
同一个规则项目,两个人分别导出 RuleApp,如果版本控制只正常提交了rules目录,没有提交完整的项目配置文件,导出的归档内容就可能不一样。
最典型的情况是:A 在本地规则项目里加了 XOM 项目依赖,B 检出后没有那个依赖,导出时 Class 没被打进去,部署后运行期才炸。处理办法是,规则项目里跟构建路径相关的配置文件,一律纳入版本控制;新成员加入时,用 SCM 里固定的项目集合拉代码,不要自己在本机“补”依赖。
我这些年做老规则项目改造,最深的一点体会是:JRules 这种重型规则引擎框架,真正的复杂度不在规则语言本身,而在项目资产的“组织方式”上。XOM、BOM、Vocabulary、RuleSet 之间的关系一旦理顺,后面写规则就是水到渠成的事。反过来,关系没理顺,哪怕规则语法背得再熟,项目也走不远。这篇把新建规则项目讲透了,下一篇可以接着聊怎么把一套 RuleSet 设计成能支撑长期变化的决策服务。