⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Fight/jujue-ifelse-zheigeqingliangguizeyinqing-Easy-Rulesrangdaimagengqingshuang/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

if-else 写多了,真的会烦

做业务系统久了,多少都遇到过这种代码:

if (user.isVip()) {
if (order.getAmount() > 200) {
if (order.hasPromoItem()) {
order.applyDiscount(0.8);
}
} else if (isDoubleEleven(order)) {
order.applyDiscount(0.9);
}
} else {
// 普通用户,又一堆...
}

一开始还好。但产品每隔两周改一次促销规则,一个月下来这方法涨到两三百行。最难受的是改规则还得上线,稍不注意就出问题。

后来换成规则引擎之后,新增规则不用改老代码,测试也容易多了。今天聊的 Easy Rules 是 Java 里比较简单的规则引擎——上手快、代码量小、中小项目很合适。

Easy Rules 是什么

官方定位:the simple, stupid rules engine for Java。

核心思路来自 Martin Fowler 的文章:把条件和动作放到一组对象里,遍历、判断条件、执行动作——这就是规则引擎。Easy Rules 把这个过程封装好,给你一套干净的 API。

项目地址:https://github.com/j-easy/easy-rules

几个关键数据:核心 jar 只有 217KB;基于 POJO 和注解开发,零学习曲线;支持 MVEL、SpEL、JEXL 表达式语言,规则可外置到配置文件;支持规则组合;MIT 协议完全开源。

说明:Easy Rules 从 2020 年底进入维护模式(只修 Bug 不加功能),最新稳定版 4.1.0。不影响使用,只是别指望大更新。

引入依赖:

<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>4.1.0</version>
</dependency>

按需加表达式语言模块:

<!-- MVEL 表达式 -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-mvel</artifactId>
<version>4.1.0</version>
</dependency>

<!-- SpEL(Spring 表达式) -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-spel</artifactId>
<version>4.1.0</version>
</dependency>

<!-- 组合规则 -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-support</artifactId>
<version>4.1.0</version>
</dependency>

最快验证——判断下雨要不要带伞:

@Rule(name = "weather rule", description = "if it rains then take an umbrella")
public class WeatherRule {

@Condition
public boolean itRains(@Fact("rain") boolean rain) {
return rain;
}

@Action
public void takeAnUmbrella() {
System.out.println("It rains, take an umbrella!");
}
}

public class Test {
public static void main(String[] args) {
Facts facts = new Facts();
facts.put("rain", true);

Rules rules = new Rules();
rules.register(new WeatherRule());

RulesEngine engine = new DefaultRulesEngine();
engine.fire(rules, facts);
// 输出:It rains, take an umbrella!
}
}

三步走:定义规则 → 放事实 → 触发引擎。

三个核心概念

整个框架就三样东西:

Rule(规则):条件(Condition)+ 动作(Action)。条件返回 boolean,为 true 时执行动作。规则有名字、描述和优先级三个属性——数值越小优先级越高,默认 Integer.MAX_VALUE。

Facts(事实):key-value 容器,底层就是 HashMap<String, Object>。规则里通过 @Fact("key") 注入对应值。

Facts facts = new Facts();
facts.put("age", 25);
facts.put("vip", true);

// 规则里这样用
@Condition
public boolean check(@Fact("age") int age, @Fact("vip") boolean vip) {
return age >= 18 && vip;
}

注意:@Fact 引用的 key 不存在时,引擎直接抛 RuntimeException。

RulesEngine(引擎):两种实现。DefaultRulesEngine 按优先级跑一遍,最常用。InferenceRulesEngine 反复跑直到没有规则被触发,适合推断场景。大多数业务用 Default 就够了。

定义规则的三种方式

方式一:注解(最常用)

@Rule(name = "vip discount", priority = 1)
public class VipDiscountRule {

@Condition
public boolean isVip(@Fact("user") User user) {
return user.isVip();
}

@Action(order = 1)
public void applyDiscount(@Fact("order") Order order) {
order.applyDiscount(0.9);
}

@Action(order = 2)
public void log(@Fact("order") Order order) {
System.out.println("已对订单 " + order.getId() + " 应用 VIP 9 折");
}
}

@Rule 定义名称和优先级;@Condition 标条件方法(一个规则只能有一个);@Action 标动作方法(可多个,order 控制顺序);@Fact 注入事实值。

方式二:RuleBuilder API

不想单独建类时用:

Rule ageRule = new RuleBuilder()
.name("age rule")
.description("成年判断")
.priority(1)
.when(facts -> {
int age = facts.get("age");
return age >= 18;
})
.then(facts -> System.out.println("已成年,可以操作"))
.build();

适合动态创建或逻辑很简单的场景。

方式三:表达式语言(规则外置)

最灵活——规则写在 YAML 里,不重新部署就能改。

# weather-rule.yml
name: "weather rule"
description: "if it rains then take an umbrella"
condition: "rain == true"
actions:
- "System.out.println(\"It rains, take an umbrella!\");"

Java 加载:

MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));

Facts facts = new Facts();
facts.put("rain", true);
Rules rules = new Rules();
rules.register(weatherRule);
new DefaultRulesEngine().fire(rules, facts);

也可以用 SpEL:

Rule spelRule = new SpELRule()
.name("spel rule")
.when("#{ #facts['age'] >= 18 }")
.then("#{ T(java.lang.System).out.println('已成年') }");

SpEL 功能更强(方法调用 + 集合操作),MVEL 语法更简洁。不少团队用 MVEL + YAML 实现规则配置化。

组合规则

easy-rules-support 提供三种组合方式:

UnitRuleGroup(全部满足才执行):组内所有规则条件全部为 true 才执行所有动作。适合”VIP 且满 200”这种多条件同时满足场景。

UnitRuleGroup group = new UnitRuleGroup("vip full reduction");
group.addRule(vipRule);
group.addRule(fullAmountRule);

ActivationRuleGroup(命中第一个就停):按优先级执行,某条触发后剩下的不跑。适合互斥折扣——大单优先、VIP 次之、普通兜底。

ActivationRuleGroup discountGroup = new ActivationRuleGroup("discount rule");
discountGroup.addRule(bigOrderRule);
discountGroup.addRule(vipRule);
discountGroup.addRule(normalRule);

ConditionalRuleGroup(最高优先级当门禁):优先级最高的规则充当入口守卫,它通过后其他规则才继续。适合先校验权限再做计算。

引擎参数

DefaultRulesEngine 支持三个常用参数:

参数 含义 默认值
skipOnFirstAppliedRule 某条规则触发后跳过后面所有 false
skipOnFirstFailedRule 某条规则执行失败后停 false
skipOnFirstNonTriggeredRule 某条规则条件不满足时停 false
RulesEngineParameters parameters = new RulesEngineParameters()
.skipOnFirstAppliedRule(true)
.skipOnFirstFailedRule(true);

RulesEngine engine = new DefaultRulesEngine(parameters);

默认全部 false——引擎会把所有规则都尝试跑一遍。需要短路逻辑时再开。

集成 Spring Boot

配置类:

@Configuration
public class RuleEngineConfig {

@Bean
public RulesEngine rulesEngine() {
return new DefaultRulesEngine(
new RulesEngineParameters().skipOnFirstNonTriggeredRule(true)
);
}

@Bean
public Rules rules() {
Rules rules = new Rules();
rules.register(new VipDiscountRule());
rules.register(new FullAmountRule());
return rules;
}
}

Service 里用:

@Service
public class OrderService {

@Autowired
private RulesEngine rulesEngine;
@Autowired
private Rules rules;

public Order applyDiscount(Order order, User user) {
Facts facts = new Facts();
facts.put("order", order);
facts.put("user", user);
rulesEngine.fire(rules, facts);
return order;
}
}

如果用了 Spring Cloud Config,结合 @RefreshScope 可以做到配置更新后自动重新加载规则,不用重启。

什么时候用,什么时候别用

适合:

  • 业务规则变动频繁,希望规则和代码分离
  • 规则条数在几条到几十条,不太复杂
  • 项目不大,不想引入 Drools
  • 常见场景:电商促销、订单定价、风控过滤、工单分配、设备告警

不适合:

  • 规则数量几千条以上 + 需要高性能推理 → Drools
  • 规则涉及大量相互依赖 → 工作流引擎
  • 只有几个简单判断 → if-else 直接就好,引入规则引擎反而多一层

Easy Rules 没有很大的野心,就是把规则这件事做简单。核心三样东西:Facts 载数据、Rule 管逻辑、RulesEngine 执行。定义规则三种写法,注解最直观,表达式语言最灵活。够用就行。

文章目录
  1. 1. if-else 写多了,真的会烦
  2. 2. Easy Rules 是什么
  3. 3. 三个核心概念
  4. 4. 定义规则的三种方式
    1. 4.0.1. 方式一:注解(最常用)
    2. 4.0.2. 方式二:RuleBuilder API
    3. 4.0.3. 方式三:表达式语言(规则外置)
  • 5. 组合规则
  • 6. 引擎参数
  • 7. 集成 Spring Boot
  • 8. 什么时候用,什么时候别用