EasyRules:轻量级规则引擎的实战入门

news2026/5/17 6:19:36
1. 为什么你需要了解EasyRules如果你是一名开发者肯定遇到过这样的场景业务逻辑越来越复杂代码里充斥着大量的if-else嵌套每次修改都要小心翼翼生怕影响其他逻辑。我曾经维护过一个用户积分系统光是判断用户等级就有十几层条件判断后来新增一个特殊会员类型时差点把原有逻辑改崩。这时候你就需要一个规则引擎来解救你。但传统的规则引擎如Drools学习曲线陡峭配置复杂对于中小型项目来说有点杀鸡用牛刀的感觉。EasyRules就是为解决这个问题而生的轻量级方案它的核心代码只有几个类学习成本低却能帮你把业务规则从代码中彻底解耦。我最近在一个电商促销系统中使用EasyRules实现了优惠券规则管理原本需要一周开发的功能用EasyRules两天就搞定了而且后续业务调整时产品经理直接改YAML配置文件就能上线新规则再也不用等着开发排期了。2. 快速理解EasyRules核心概念2.1 三大核心组件EasyRules的核心设计非常简洁主要包含三个关键组件Fact可以理解为事实数据就是你的业务对象。比如在用户积分场景中用户的当前积分、注册时长、最近消费金额等都可以作为Fact。在代码中通常用Map或者POJO来表示。Rule规则的定义包含条件(condition)和动作(action)两部分。当条件满足时就会执行对应的动作。比如如果用户积分大于1000则升级为黄金会员就是一条典型规则。RulesEngine规则引擎负责评估所有规则并执行符合条件的动作。EasyRules提供了两种引擎实现默认引擎会按顺序执行所有符合条件的规则而流式引擎则会在第一条符合条件的规则执行后就停止。// 示例用Map存储Fact MapString, Object facts new HashMap(); facts.put(userScore, 1200); facts.put(userAge, 2); // 示例一个简单的Rule定义 Rule(name goldUserRule, description 如果积分超过1000就是黄金用户) public class GoldUserRule { Condition public boolean when(Fact(userScore) int score) { return score 1000; } Action public void then() { System.out.println(恭喜升级为黄金会员); } }2.2 与传统if-else的对比为了更直观地理解EasyRules的价值我们来看一个实际对比。假设要实现以下业务规则积分≥1000黄金会员积分≥500且注册满1年白银会员积分≥200青铜会员传统if-else实现if(score 1000) { user.setLevel(gold); } else if(score 500 user.getRegisterYears() 1) { user.setLevel(silver); } else if(score 200) { user.setLevel(bronze); }使用EasyRules实现Rule(name goldRule, priority 1) public class GoldRule { Condition public boolean when(Fact(score) int score) { return score 1000; } Action public void then() { user.setLevel(gold); } } Rule(name silverRule, priority 2) public class SilverRule { Condition public boolean when(Fact(score) int score, Fact(registerYears) int years) { return score 500 years 1; } Action public void then() { user.setLevel(silver); } } // 其他规则类似...优势显而易见规则之间完全解耦新增或修改规则不会影响其他规则规则定义更接近自然语言可读性更好规则可以动态加载无需重新部署应用。3. 三种方式玩转规则定义EasyRules最强大的地方在于它提供了多种规则定义方式适应不同场景需求。下面我就以用户积分等级评定为例分别演示三种主流方式。3.1 注解方式最适合Java开发者注解方式是最直观的Java原生支持方式适合规则相对固定的场景。我在实际项目中最常用的就是这种方式它的优点是类型安全IDE支持好重构方便。Rule(name vipRule, description 特殊VIP用户规则, priority 1) public class VipUserRule { Condition public boolean isVip( Fact(score) int score, Fact(consumption) double consumption) { return score 5000 || consumption 10000; } Action public void setVip() { user.setVip(true); System.out.println(授予VIP身份); } } // 使用方式 RulesEngineParameters params new RulesEngineParameters() .skipOnFirstAppliedRule(true); // 使用流式引擎 RulesEngine engine new DefaultRulesEngine(params); Rules rules new Rules(); rules.register(new GoldUserRule()); rules.register(new VipUserRule()); engine.fire(rules, facts);实战技巧通过priority属性控制规则执行顺序数字越小优先级越高使用skipOnFirstAppliedRule参数可以开启流式模式匹配到第一条规则后就停止规则类可以像普通Spring Bean一样被管理方便集成到现有系统中3.2 流式API适合动态规则构建当你需要根据运行时条件动态构建规则时流式API是不二之选。我曾经做过一个动态促销系统规则需要根据库存情况实时调整流式API完美解决了这个问题。Rule weatherRule new RuleBuilder() .name(weatherPromotionRule) .description(下雨天雨伞打折) .when(facts - rainy.equals(facts.get(weather))) .then(facts - { product.setDiscount(0.8); System.out.println(启动雨天促销); }) .build(); Rule stockRule new RuleBuilder() .name(clearanceRule) .description(库存清理规则) .when(facts - (int)facts.get(stock) 1000) .then(facts - { product.setDiscount(0.6); System.out.println(启动清仓促销); }) .build(); Rules rules new Rules(); rules.register(weatherRule); rules.register(stockRule); engine.fire(rules, facts);适用场景规则需要根据用户输入或其他运行时条件动态生成规则条件简单不需要复杂逻辑判断需要快速原型开发时3.3 YAML配置业务人员友好的方式YAML方式是我最推荐给需要业务人员参与规则配置的场景。产品经理可以直接修改YAML文件调整业务规则完全不需要开发介入。在我们的电商系统中促销规则都是用这种方式管理的。name: newUserRule description: 新用户首单优惠 priority: 1 condition: user.new true order.first true actions: - order.discount 0.9 - system.out.println(新用户首单享受9折)加载YAML规则Rules rules YamlRuleFactory.createRulesFrom( new File(rules/new-user-rule.yml)); engine.fire(rules, facts);最佳实践将不同业务领域的规则放在不同的YAML文件中使用版本控制管理规则变更历史可以配合Spring Cloud Config实现规则的热更新4. 实战用户积分等级系统现在让我们把这些知识综合起来实现一个完整的用户积分等级系统。这个案例来自我实际参与过的一个会员体系重构项目。4.1 系统需求分析我们需要实现以下业务规则基础等级规则积分≥5000钻石会员积分≥3000白金会员积分≥1000黄金会员积分≥500白银会员积分≥100青铜会员特殊规则连续签到7天提升一个等级最高到黄金最近30天消费满5000元直接升级为白金降级规则连续90天无消费降一级积分低于当前等级要求降级到对应等级4.2 实现步骤首先定义我们的Fact对象public class User { private String userId; private int score; private String level; private int consecutiveCheckins; private double last30DaysConsumption; private LocalDate lastConsumptionDate; // getters/setters }然后实现核心规则以注解方式为例Rule(name diamondRule, priority 1) public class DiamondRule { Condition public boolean when(Fact(user) User user) { return user.getScore() 5000; } Action public void then(Fact(user) User user) { if(!diamond.equals(user.getLevel())) { user.setLevel(diamond); sendNotification(user, 恭喜升级为钻石会员); } } } Rule(name checkinBoostRule, priority 10) public class CheckinBoostRule { Condition public boolean when(Fact(user) User user) { return user.getConsecutiveCheckins() 7 !gold.equals(user.getLevel()); } Action public void then(Fact(user) User user) { String newLevel calculateUpgradedLevel(user.getLevel()); user.setLevel(newLevel); user.setConsecutiveCheckins(0); // 重置签到计数 } private String calculateUpgradedLevel(String current) { // 升级逻辑实现 } }最后是引擎配置和执行public class LevelService { private RulesEngine engine; private Rules rules; PostConstruct public void init() { engine new DefaultRulesEngine( new RulesEngineParameters() .skipOnFirstNonTriggeredRule(false)); rules new Rules(); rules.register(new DiamondRule()); rules.register(new CheckinBoostRule()); // 注册其他规则... } public void evaluate(User user) { Facts facts new Facts(); facts.put(user, user); engine.fire(rules, facts); } }4.3 遇到的坑与解决方案在实际实现过程中我遇到过几个典型问题规则执行顺序问题最初没有设置priority导致降级规则先于升级规则执行。解决方案是为所有规则明确设置优先级确保升级规则先执行。性能问题当规则数量超过100条时引擎执行时间明显变长。通过以下方式优化将规则按业务领域分组不同场景使用不同的规则组使用skipOnFirstNonTriggeredRule参数减少不必要的评估对高频规则设置更高的优先级规则冲突问题两条规则的条件有重叠时可能产生冲突。我们引入了规则冲突检测机制在测试阶段就发现并解决这类问题。5. 进阶技巧与最佳实践经过多个项目的实践我总结出以下经验能帮你更好地使用EasyRules。5.1 如何组织大型规则集当规则数量增多时良好的组织方式至关重要按业务领域分包例如将用户相关规则放在user包下订单规则放在order包下。使用规则组通过组合模式将相关规则打包Rules userLevelRules new Rules(); userLevelRules.register(new GoldRule()); userLevelRules.register(new SilverRule()); Rules promotionRules new Rules(); promotionRules.register(new CouponRule()); promotionRules.register(new DiscountRule()); // 按需执行不同规则组 engine.fire(userLevelRules, userFacts); engine.fire(promotionRules, orderFacts);版本化管理为规则定义版本号支持多版本规则共存便于灰度发布和回滚。5.2 测试策略规则引擎的测试需要特别关注单元测试每个规则确保每个规则的condition和action都正确Test public void testGoldRule() { // 准备 GoldRule rule new GoldRule(); User user new User(); user.setScore(1200); Facts facts new Facts(); facts.put(user, user); // 执行 验证 assertTrue(rule.when(facts)); rule.then(facts); assertEquals(gold, user.getLevel()); }集成测试规则组合验证多个规则一起执行时的效果。性能测试模拟生产环境的数据量确保引擎性能达标。5.3 与Spring集成在Spring项目中使用EasyRules非常方便将规则类声明为Spring组件Component Rule(name springRule) public class SpringIntegrationRule { Autowired private UserService userService; // 规则实现... }自动发现并注册所有规则Configuration public class RulesConfig { Autowired private ListObject ruleBeans; // 收集所有带有Rule注解的Bean Bean public Rules rules() { Rules rules new Rules(); ruleBeans.forEach(rules::register); return rules; } }在Service中使用Service public class UserLevelService { Autowired private RulesEngine engine; Autowired private Rules rules; public void evaluateUser(User user) { Facts facts new Facts(); facts.put(user, user); engine.fire(rules, facts); } }6. 什么时候该用或不该用EasyRules虽然EasyRules很强大但它并不是银弹。根据我的经验以下场景特别适合使用EasyRules业务规则频繁变更比如促销活动、费率计算等经常需要调整的业务逻辑。需要业务人员参与规则配置通过YAML方式让非技术人员也能参与规则管理。规则数量中等几十到几百条规则太多时可能需要考虑更专业的规则引擎。而不适合的场景包括超高性能要求规则引擎毕竟有额外开销对性能要求极高的核心链路可能需要更直接的代码实现。非常简单的业务逻辑只有两三条固定规则时直接if-else可能更简单。需要复杂的规则推理如需要处理复杂的规则网络和推理链时Drools等更专业的引擎会更合适。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2608847.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…