Easy Rules是一个简单而强大的Java规则引擎,提供以下功能:
- 轻量级框架和易于学习的API
- 基于POJO的开发与注解的编程模型
- 定义抽象的业务规则并轻松应用它们
- 支持从简单规则创建组合规则的能力
- 支持使用表达式语言(如MVEL和SpEL)定义规则的能力
为何选择Easy Rules规则引擎
1. 传统if - else编程的困境
案例一:电商满减规则频繁变更(真实生产场景重现)
在电商业务的实际运营中,促销规则的频繁变更是一个常见的问题。假设某电商平台有如下促销规则:
这种传统的硬编码方式存在诸多痛点:
- 维护困难:每当市场部调整规则时,开发者需要在大量的代码中艰难地寻找逻辑修改点,这不仅效率低下,还容易出错。
- 发版风险高:发版频率极高,可能一个月需要进行6次规则修改和上线操作,每次上线都伴随着一定的风险,如代码冲突、功能异常等。
- 协作问题:在多人协作开发时,由于代码结构复杂,很容易引发代码冲突,增加了开发和维护的难度。
案例二:物联网设备告警条件嵌套难题
在物联网设备监控系统中,复杂的告警条件嵌套也是一个常见的问题。某工厂设备监控系统需要进行如下判断:
这种代码结构带来了以下问题:
- 调试困难:在调试过程中,断点需要穿透10层条件判断,调试难度极大,耗费大量时间。
- 扩展性差:当需要新增“电压波动 > 10%”这样的条件时,需要重构整个逻辑,开发成本高。
- 知识传递困难:交接文档需要绘制3页流程图才能清晰说明规则逻辑,给知识传递带来了很大的困难。
可视化对比(代码量的显著优化)
代码量对比
2. 轻量级规则引擎的优势
场景化演示:从复杂到简洁的转变
场景转变
核心优势解析
- 解耦的智慧
规则与业务分离:规则与业务代码实现物理隔离,可以将规则存储在独立文件或数据库中,使代码结构更加清晰。这样,业务代码专注于业务逻辑的处理,而规则代码则负责规则的定义和管理。
动态加载规则:修改规则无需重新编译部署,支持动态加载规则。以下是一个动态加载规则的示例:
- 可读性的提升
自描述性规则:规则具有自描述性,每个规则都可以看作是一个独立的文档,便于理解和维护。开发者可以通过规则的名称、描述和条件等信息,快速了解规则的用途和逻辑。
决策流程可视化:支持决策流程可视化,可以自动生成规则关系图。例如:
- 扩展性的保障
零侵入式扩展:新增规则对现有代码零侵入,只需添加新的Rule类即可。这使得系统的扩展性得到了极大的提升,开发者可以根据业务需求随时添加新的规则。
多规则源支持:支持混合多种规则源,例如数据库、YAML文件和注解。以下是一个YAML规则文件的示例:
定义规则
大多数业务规则可以由以下定义表示:
- 名称:规则命名空间中的唯一规则名称
- 说明:规则的简要说明
- 优先级:相对于其他规则的规则优先级
- 事实:去匹配规则时的一组已知事实
- 条件:为了匹配该规则,在给定某些事实的情况下应满足的一组条件
- 动作:当条件满足时要执行的一组动作(可以添加/删除/修改事实)
Easy Rules为定义业务规则的每个关键点提供了抽象。
在Easy Rules中,一个规则由Rule
接口表示:
evaluate方法封装了必须求值为TRUE才能触发规则的条件。
execute方法封装了在满足规则条件时应执行的操作。条件和动作Condition
andAction
接口表示。
规则可以用两种不同的方式定义:
- 通过在POJO上添加注释,以声明方式定义
- 通过RuleBuilder API,以编程方式定义
用注解定义规则
这些是定义规则的最常用方法,但如果需要,还可以实现Rule
i接口或继承BasicRule
类。
@Condition注解标记计算规则条件的方法。此方法必须是公共的,可以有一个或多个用@Fact注解的参数,并返回布尔类型。只有一个方法能用@Condition注解。
@Action注解标记要执行规则操作的方法。规则可以有多个操作。可以使用order属性按指定的顺序执行操作。默认情况下,操作的顺序为0。
2. 用RuleBuilder API定义规则
在这个例子中, Condition实例condition,Action实例是action1和action2。
定义事实
Facts API是一组事实的抽象,在这些事实上检查规则。在内部,Facts实例持有HashMap<String,Object>,这意味着:
- 事实需要命名,应该有一个唯一的名称,且不能为空
- 任何Java对象都可以充当事实
这里有一个实例定义事实:
Facts 能够被注入规则条件,action 方法使用 @Fact
注解. 在下面的规则中,rain
事实被注入itRains方法的rain
参数:
Facts
类型参数 被注入已知的 facts中 (像action方法takeAnUmbrella
一样).
如果缺少注入的fact, 这个引擎会抛出 RuntimeException
异常.
定义规则引擎
从版本3.1开始,Easy Rules提供了RulesEngine接口的两种实现:
- DefaultRulesEngine:根据规则的自然顺序(默认为优先级)应用规则。
- InferenceRulesEngine:持续对已知事实应用规则,直到不再应用规则为止。
创建一个规则引擎
要创建规则引擎,可以使用每个实现的构造函数:
然后,您可以按以下方式触发注册规则:
规则引擎参数
Easy Rules 引擎可以配置以下参数:
图片
skipOnFirstAppliedRule
:告诉引擎规则被触发时跳过后面的规则。skipOnFirstFailedRule
:告诉引擎在规则失败时跳过后面的规则。skipOnFirstNonTriggeredRule
:告诉引擎一个规则不会被触发跳过后面的规则。rulePriorityThreshold
:告诉引擎如果优先级超过定义的阈值,则跳过下一个规则。版本3.3已经不支持更改,默认MaxInt。
可以使用RulesEngineParameters API指定这些参数:
如果要从引擎获取参数,可以使用以下代码段:
这允许您在创建引擎后重置引擎参数。
5分钟极速入门(Hello World版)
1. 环境搭建(手把手教学)
为什么选择Maven依赖?Easy Rules的核心库仅有 217KB,不会造成项目臃肿。只需在pom.xml
中添加:
验证是否成功: 在IDE中新建RulesEngine engine = new DefaultRulesEngine();
若无报错,则环境配置成功!
2. 第一个规则实战(带逐行解析)
场景背景: 假设我们正在开发智能家居系统,需要根据湿度传感器数据触发雨天提醒。
代码深度解读:
图片
执行过程全解:
3. 可视化规则执行流程(小白秒懂版)
完整执行链路图示:
图片
关键点提醒:
- 一个Facts对象可承载多个数据:
- 多个规则会按优先级顺序执行(默认优先级=0)
- 使用
注解调整执行顺序:
新手常见问题QA:规则没触发怎么办?
- 检查
@Fact
名称是否与put时一致 - 确认
@Condition
方法返回true - 添加日志打印调试:
如何同时处理多个规则?
需要我展示如何扩展这个案例,比如增加温度规则形成组合条件吗?比如"湿度>80% 且 温度>30℃"触发高温高湿预警?
6大经典场景深度解析
场景1:电商促销系统(组合优惠精算)
案例3进阶实现:VIP折扣与满减叠加计算
避坑指南:
- 使用
@Priority
控制执行顺序(数值越大越先执行) - 折扣计算需采用乘法叠加而非减法,避免出现0元订单
- 在动作中增加日志记录,审计实际优惠金额
场景2:物联网报警系统(多级联动)
案例4优化版:带设备状态判断的三级报警
图片
实战技巧:
- 设备维护状态作为独立Fact传递
- 优先处理高风险规则(priority=3)
- 动作中集成多种通知渠道(短信/邮件/看板)
场景3:会员等级系统(混合规则源)
案例5增强方案:YAML+注解混合使用
图片
集成方法:
场景4:工单分配系统(动态派单)
案例6增强版:基于值班表的动态分配
设计亮点:
- 值班表作为独立Fact,实时反映工程师状态
- 自动标记工程师忙碌状态,避免重复分配
- 可扩展支持轮询、负载均衡等分配策略
场景5:风控预警系统(时序检测)
案例7优化版:时间窗口滑动检测
图片
性能优化:
- 使用
@Fact
注入预处理的时序数据 - 采用BloomFilter快速过滤低风险设备
- 异步执行风险处理动作
场景6:游戏战斗系统(状态管理)
案例8增强版:连招技能状态机
注意事项:
- 使用Apache Commons的CircularFifoQueue控制队列长度
- 动作中重置状态避免重复触发
- 集成音效/特效等游戏元素
架构师扩展包:
- 规则模板技术:
- 规则性能监控:
Spring Boot集成
配置自动加载:
在controller中测试:
在生产中我们还可以将规则配置设置为热更新,以@RefreshScope
+ Spring Cloud Config的方式,这样在配置更新时会自动加载。
总结
Easy Rules 非常适合需要快速实现业务规则引擎的场景。对于中小型项目,Easy Rules 的简单性和灵活性是一大优势。
如果项目规则复杂或者性能要求较高,可以考虑结合 Drools 等更强大的规则引擎使用。