1. 为什么选择easy-rules作为业务规则引擎
在业务系统开发中,我们经常遇到需要频繁变更的业务规则。传统硬编码方式每次修改都需要重新部署,而规则引擎将业务决策逻辑从应用程序代码中分离出来,实现了动态规则管理。easy-rules正是为Java开发者设计的一款轻量级规则引擎。
相比Drools等重型规则引擎,easy-rules的核心优势在于:
- 极简API设计:整个库只有不到10个核心类
- 零外部依赖:纯粹基于Java 8+开发
- 学习曲线平缓:掌握基础概念只需2小时
- 灵活扩展:支持多种规则定义方式
提示:当规则数量超过100条或需要复杂规则流时,建议考虑Drools等专业引擎。但对于90%的中小型业务场景,easy-rules完全够用。
2. 快速搭建easy-rules环境
2.1 基础依赖配置
在Maven项目中引入最新版本(截至2024年1月为4.1.0):
xml复制<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>4.1.0</version>
</dependency>
如果需要支持规则定义文件,还需添加:
xml复制<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-support</artifactId>
<version>4.1.0</version>
</dependency>
2.2 核心概念速成
easy-rules的三个核心抽象:
- Fact:规则处理的数据对象,本质是键值对集合
- Rule:包含条件(when)和动作(then)的业务规则
- RulesEngine:执行规则推理的引擎实例
典型使用模式:
java复制// 创建事实
Facts facts = new Facts();
facts.put("temperature", 30);
// 创建规则
Rule weatherRule = new RuleBuilder()
.name("高温预警规则")
.when(f -> (int)f.get("temperature") > 28)
.then(f -> System.out.println("触发高温预警"))
.build();
// 执行规则
RulesEngine engine = new DefaultRulesEngine();
engine.fire(new Rules(weatherRule), facts);
3. 四种规则定义方式实战
3.1 注解式规则(推荐)
最符合Java开发者习惯的方式:
java复制@Rule(name = "VIP折扣规则", description = "VIP客户享受额外折扣")
public class VipDiscountRule {
@Condition
public boolean isVip(@Fact("user") User user) {
return user.isVip();
}
@Action
public void giveDiscount(@Fact("order") Order order) {
order.applyDiscount(0.9);
}
}
注意:方法参数需要通过@Fact注解明确指定事实名称
3.2 流式API规则
适合简单规则的快速定义:
java复制Rule ageCheckRule = new RuleBuilder()
.name("年龄校验规则")
.description("检查用户是否成年")
.when(facts -> (int)facts.get("age") < 18)
.then(facts -> System.out.println("未成年用户警告"))
.build();
3.3 YAML规则文件
将规则配置外部化:
yaml复制name: "雨天促销规则"
description: "下雨天自动激活雨具促销"
priority: 1
condition: "weather == 'rainy'"
actions:
- "discountMap.put('umbrella', 0.7)"
- "System.out.println('雨具7折优惠已激活')"
加载方式:
java复制Rules rules = YamlRuleFactory.createRulesFrom(
new FileReader("rules/weather-rule.yml"));
3.4 SpEL表达式规则
适合需要动态表达式的场景:
java复制MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule rule = ruleFactory.createRule(
new FileReader("rules/mvel-rule.yml"));
4. 高级特性深度应用
4.1 规则优先级控制
三种优先级设置方式:
- 通过@Rule注解的priority属性
- 规则定义文件的priority字段
- 继承Priority接口实现getPriority()
java复制@Rule(priority = 1) // 数字越小优先级越高
public class HighPriorityRule {
// ...
}
4.2 复合规则组合
使用UnitRuleGroup实现规则组:
java复制UnitRuleGroup addressValidationGroup = new UnitRuleGroup(
"地址校验规则组",
new ZipCodeRule(),
new CityRule(),
new StreetRule()
);
Rules rules = new Rules();
rules.register(addressValidationGroup);
4.3 引擎执行参数调优
配置规则引擎行为:
java复制RulesEngineParameters params = new RulesEngineParameters()
.skipOnFirstAppliedRule(true) // 匹配到第一条规则后跳过后续规则
.skipOnFirstFailedRule(false) // 是否在第一条失败规则处停止
.skipOnFirstNonTriggeredRule(false);
RulesEngine engine = new DefaultRulesEngine(params);
5. 生产环境最佳实践
5.1 规则版本管理方案
推荐目录结构:
code复制/rules
/v1
discount-rule.yml
weather-rule.yml
/v2
discount-rule.yml
weather-rule.yml
current -> v2 # 符号链接指向当前版本
动态加载实现:
java复制public Rules loadCurrentRules() throws Exception {
Path currentLink = Paths.get("rules/current");
Path realPath = Files.readSymbolicLink(currentLink);
return YamlRuleFactory.createRulesFrom(
new FileReader(realPath.resolve("all-rules.yml").toString()));
}
5.2 性能优化技巧
- 事实对象优化:
java复制// 反模式:频繁修改的大对象
facts.put("user", user);
// 正解:仅传递必要字段
facts.put("userAge", user.getAge());
facts.put("userVip", user.isVip());
- 规则缓存机制:
java复制private static Rules cachedRules;
public synchronized Rules getRules() {
if (cachedRules == null || checkRulesModified()) {
cachedRules = reloadRules();
}
return cachedRules;
}
5.3 监控与日志方案
自定义监听器实现:
java复制public class MetricRuleListener implements RuleListener {
private final MeterRegistry registry;
@Override
public boolean beforeExecute(Rule rule, Facts facts) {
registry.timer("rule.execution.time")
.record(() -> executeRule(rule, facts));
return true;
}
}
// 注册监听器
engine.registerRuleListener(new MetricRuleListener(prometheusMeterRegistry));
6. 典型业务场景实现
6.1 电商促销系统
折扣规则组合示例:
java复制CompositeRule composite = new CompositeRule();
composite.addRule(new VipDiscountRule());
composite.addRule(new FestivalPromotionRule());
composite.addRule(new InventoryClearanceRule());
RulesEngine engine = new DefaultRulesEngine();
engine.fire(new Rules(composite), orderFacts);
6.2 风控决策系统
实现规则决策流:
java复制SequentialRulesEngine engine = new SequentialRulesEngine();
Rules rules = new Rules(
new BlackListCheckRule(),
new CreditScoreRule(),
new DebtRatioRule()
);
engine.fire(rules, applicantFacts);
6.3 工单路由系统
基于标签的路由规则:
java复制@Rule
public class TechnicalTicketRule {
@Condition
public boolean isTechnical(@Fact("tags") Set<String> tags) {
return tags.contains("technical");
}
@Action
public void routeToTechTeam(@Fact("ticket") Ticket ticket) {
ticket.assignTo("tech-support");
}
}
7. 常见问题排查指南
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 规则未触发 | 事实键名不匹配 | 检查@Fact注解与put键名一致性 |
| 执行顺序异常 | 未设置优先级 | 为规则添加@Rule(priority=1) |
| 性能下降 | 事实对象过大 | 只传递必要字段而非整个对象 |
| 内存泄漏 | 规则缓存未清理 | 实现规则热更新机制 |
| SpEL解析失败 | 表达式语法错误 | 使用MVEL调试器验证表达式 |
8. 扩展与集成方案
8.1 与Spring Boot集成
自动配置实现:
java复制@Configuration
public class EasyRulesConfig {
@Bean
@ConditionalOnMissingBean
public RulesEngine rulesEngine() {
return new DefaultRulesEngine();
}
@Bean
public Rules rules(ResourcePatternResolver resolver) throws Exception {
Resource[] resources = resolver.getResources("classpath:rules/*.yml");
YamlRuleFactory factory = new YamlRuleFactory();
return Arrays.stream(resources)
.map(r -> factory.createRule(new InputStreamReader(r.getInputStream())))
.collect(Collectors.toCollection(Rules::new));
}
}
8.2 规则可视化编辑
基于JSON Schema的规则定义:
json复制{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": { "type": "string" },
"condition": { "type": "string" },
"actions": {
"type": "array",
"items": { "type": "string" }
}
}
}
前端配合规则编辑器如React-JSONSchema-Form即可实现可视化编辑。
8.3 分布式规则服务
基于Spring Cloud的实现架构:
code复制[Client App] -> [Rule Service] -> [Rule Repository]
↑
[Rule Console] ←──┘
核心接口设计:
java复制public interface RuleService {
RuleExecutionResult execute(String ruleSetId, Map<String, Object> facts);
void deployRuleSet(RuleSet ruleSet);
RuleSet getRuleSet(String ruleSetId);
}
