JBoss Drools动态生成规则多过滤器拼接实现方案咨询
首先明确核心问题:Drools自带的原生规则模板(即配合决策表使用的模板引擎)是极简的字符串替换实现,没有内置循环、条件判断这类流程控制语法,仅支持按数据行逐行替换@{xxx}格式的占位符,所以你尝试直接传入过滤器列表、在原生模板内遍历拼接的方案本身是走不通的,这是你之前尝试失败的根本原因。
下面针对你梳理的三个思路逐一说明可行性,同时给出落地建议:
各方案优劣对比
1. 推荐PoC阶段优先选择:业务代码层预拼接过滤条件
也就是你列的第一个思路,是当前场景下成本最低、稳定性最高的实现方式,完全适配Drools原生模板的设计逻辑。
- 实现逻辑:在Java业务代码层,先把用户选中的所有过滤器按照Drools LHS语法规则拼接成完整的条件字符串,比如同时选中客户、客户组两个过滤条件时,直接拼接为
customer == 'TestCustomer' && custGroup == "TestGroup",把这个完整字符串作为你现有模板中@{rule}占位符的传入参数即可,和你已经跑通的单条件逻辑完全兼容,不需要修改现有模板结构。 - 注意事项:拼接过程中要做好类型处理:字符串类型的字段值要包裹单/双引号、数值/布尔类型不需要加引号,条件之间固定用
&&连接,拼接完成后可以先做简单的语法校验,避免生成的规则存在语法错误。 - 优势:不需要引入额外依赖、不需要调整现有模板结构、调试成本极低,在业务代码打断点就能直接看到拼接完成的条件内容,出问题排查快,完全满足PoC阶段的快速验证需求。
2. 直接排除:按过滤器数量维护多套模板
也就是你列的第二个思路,完全不具备可维护性。可选过滤器数量一旦超过5个,需要维护的模板数量就会达到几十上百个,后续每新增一个可选过滤器,需要新增的模板量就会翻一倍,维护成本呈指数级上升,没有任何落地价值。
3. 需要模板内遍历的实现方式:替换通用模板引擎
如果你一定要把条件拼接逻辑收敛到模板层,不要用Drools自带的简易模板,换成FreeMarker、Velocity这类成熟的通用模板引擎,先通过模板渲染生成完整的.drl规则文件文本,再把渲染好的规则内容传给Drools编译加载即可,这类通用模板引擎原生支持集合遍历、条件判断等逻辑。
以FreeMarker为例,模板可以按如下方式编写,直接遍历过滤器列表拼接条件:
package test.template; import test.domain.ProductDiscountRequest; <#list ruleConfigList as config> rule "Value discount on customer_${config?index}" date-effective "${config.effectiveDate}" date-expires "${config.expirationDate}" when requestObject : ProductDiscountRequest( <#list config.filters as filter> ${filter.fieldExpression} <#if filter?has_next> && </#if> </#list> && totalPrice > ${config.totalPriceValue} ); then requestObject.addDiscount(5.0d); end </#list>
传入的参数中,每个规则配置对象携带filters列表,列表中存储每个过滤条件的表达式片段,模板遍历过程中会自动在相邻条件间拼接&&连接符,最终生成符合语法的完整规则。
- 注意事项:这种方案本质是绕过Drools自带模板能力,自行完成规则文本生成,灵活性最高,但需要自行承担规则语法校验的逻辑,避免渲染出无效的DRL内容导致Drools编译失败。
生产级更优方案参考
如果PoC验证通过、后续要上线承载更复杂的动态规则场景,不建议用字符串拼接的方式生成规则,可以直接用Drools提供的原生规则描述API动态构建规则,完全避免字符串拼接带来的语法风险:
// 1. 创建规则描述对象 RuleDescr dynamicRule = new RuleDescr("Dynamic discount rule"); dynamicRule.addAttribute("date-effective", "25-Jun-2022 16:00"); dynamicRule.addAttribute("date-expires", "26-Jun-2030 8:00"); // 2. 构建when部分的匹配模式:匹配ProductDiscountRequest对象 PatternDescr requestPattern = new PatternDescr("ProductDiscountRequest", "requestObject"); // 遍历所有过滤器,逐个添加约束条件 for (Filter filter : selectedFilters) { requestPattern.addConstraint(new ExprConstraintDescr(filter.toExpression())); } // 添加总价判断约束 requestPattern.addConstraint(new ExprConstraintDescr("totalPrice > " + totalPriceThreshold)); dynamicRule.getLhs().addDescr(requestPattern); // 3. 构建then部分的执行逻辑 dynamicRule.setConsequence("requestObject.addDiscount(5.0d);"); // 4. 将构建完成的规则描述对象编译加载到KieBase即可
这种方式不需要维护模板、不需要手动拼接DRL文本,所有规则结构都通过结构化API构建,出错概率更低,适合动态性要求高的生产场景。
内容的提问来源于stack exchange,提问作者Cristian

