如何使用Drools遍历List集合并依据业务规则筛选最优对象
实现方案
Drools 完全支持多实例集合的筛选、比对、最优值评选能力,完全匹配你剥离业务硬编码、支持业务人员自主维护规则的需求,不需要回退到手写Stream过滤的实现方式。
前置准备:补全可正常使用的Java模型
你贴的类定义缺字段名和泛型声明,补全后遵循JavaBean规范补全构造方法、getter/setter即可直接使用:
public class BesPeople { private List<Person> personList; // 无参构造、全参构造、getter、setter省略 } // 类名建议遵循大驼峰规范,首字母大写 public class Person { private int height; private int weight; private int shoesize; private String votingRecord; // 可选:规则计算临时用的分数字段,标记为瞬时字段不影响业务存储 private transient int score; // 无参构造、全参构造、getter、setter省略 }
传参时两种方式都支持:既可以把包装了人员列表的BesPeople实例插入规则会话,也可以把5个Person实例逐个insert到会话中。
核心实现逻辑
你之前查到的单对象规则示例只是Drools最基础的用法,针对多实例选优的场景,Drools原生支持三类成熟写法,不需要额外开发复杂插件:
- 用
from关键字直接遍历集合,配合not条件做极值匹配,零侵入原有业务模型 - 先按业务规则给每个实例计算综合得分,再匹配得分最高的实例,逻辑清晰最适合业务人员维护
- 用
accumulate聚合函数直接计算全局阈值,再匹配符合阈值的目标实例,适合大数据量的高性能场景
可直接运行的规则示例
最推荐用评分法实现,规则可读性最高,业务人员调整权重、加减规则项的学习成本最低。比如业务规则为「身高每高1cm加1分、体重在60-70kg区间加20分、投票记录无异常加30分、鞋码在40-42区间加10分,选总分最高的为最优人员」,对应的drl规则文件内容如下:
package com.business.rules import com.yourpackage.Person; import com.yourpackage.BesPeople; // 定义全局变量,用来向Java侧返回最终选出的最优人员 global Person bestPerson; // 规则1:计算所有人员的综合评分,优先级最高先执行 rule "calc_every_person_score" salience 100 when $person : Person() then int score = 0; // 以下所有评分规则都可由业务人员自主调整,不需要修改Java源码、不需要重新打包业务服务 score += $person.getHeight(); if ($person.getWeight() >= 60 && $person.getWeight() <=70) score += 20; if ("NORMAL".equals($person.getVotingRecord())) score +=30; if ($person.getShoesize() >=40 && $person.getShoesize() <=42) score +=10; $person.setScore(score); update($person); end // 规则2:选出评分最高的人员,评分计算完成后再执行 rule "pick_up_best_person" salience 10 when $candidate : Person( $candidateScore : score ) // 不存在比当前候选人分数更高的人员 not Person( score > $candidateScore ) then bestPerson = $candidate; end
如果你不想给Person类加临时的score字段,也可以用零侵入的集合遍历写法,比如直接选身高最高的人员:
rule "pick_up_tallest_person" when $group : BesPeople( $personList : personList ) $tallest : Person() from $personList not Person( height > $tallest.height ) from $personList then bestPerson = $tallest; end
Java侧调用示例
// 初始化Drools运行环境 KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer(); KieSession session = kieContainer.newKieSession("business-rules-session"); // 传入待处理的数据 BesPeople data = new BesPeople(); // 给data设置包含5个Person实例的列表 session.insert(data); // 如果选择逐个传入Person实例,循环insert即可 // for (Person p : personList) session.insert(p); // 绑定全局变量接收结果 Person finalBestPerson = null; session.setGlobal("bestPerson", finalBestPerson); // 执行全部规则 session.fireAllRules(); session.dispose(); // 此处finalBestPerson就是规则筛选出的最优人员,可直接用于后续业务逻辑
补充说明
- 所有业务判断、筛选、排序逻辑全部存放在drl规则文件中,Java侧代码只负责传参、接结果,后续规则调整可以完全不碰业务源码,甚至可以对接可视化规则配置平台让业务人员自行修改发布规则
- 支持任意复杂度的规则逻辑:一票否决项、多维度权重计算、同分时次级规则排序、多人同时入选等需求都可以直接在规则中实现
- 性能有充足保障:即使待筛选的人员规模达到万级,Drools的Rete匹配算法的性能也远高于手写硬编码循环的实现
内容的提问来源于stack exchange,提问作者SoftwareSavant
相关产品推荐
相关产品推荐

