easy-rules 使用文档
easy-rules 提供多规则引擎统一抽象层——MVEL(完整生产级)/ LiteFlow(可用但绕开 ConfigStorage)/ JEXL(骨架半成品)。通过
EngineRegistryPostProcessor动态 Bean 注册,在application.yml里配置引擎实例 + 存储后端组合,Spring 启动时自动批量创建。规则文本存 easy-store ConfigStorage(MVEL 引擎)或 LiteFlow 自己的 XML/脚本文件。
1. 引入依赖
<!-- 核心依赖(MVEL 引擎,compile scope) -->
<dependency>
<groupId>io.matrix</groupId>
<artifactId>easy-rules</artifactId>
<version>${revision}</version>
</dependency>
<!-- LiteFlow 引擎(optional,按需要引入) -->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
</dependency>
<!-- easy-store(compile scope,MVEL 引擎通过 ConfigStorage 加载规则) -->
<!-- 会被 easy-rules 传递引入 -->
JEXL 引擎是骨架半成品,依赖声明在 pom.xml 里(optional)但不能用。如果你看到 JEXL 相关代码,那是预留的。
2. 选择引擎
| 引擎 | 成熟度 | 规则存储 | 适用场景 |
|---|---|---|---|
| MVEL | ✅ 完整 | easy-store ConfigStorage | 单规则/条件判断、规则需持久化、动态调整 |
| LiteFlow | ✅ 可用 | LiteFlow 自己的 XML/脚本 | 链路编排、有序执行、复杂条件链 |
| JEXL | ⚠️ 半成品 | — | 暂不可用 |
推荐策略:大多数场景选 MVEL。需要链路编排(A→B→C 按顺序执行)时选 LiteFlow。
3. 配置
easy-rules 通过 EngineRegistryPostProcessor 动态注册引擎 Bean——在 application.yml 里写好 easy.engines.instances,Spring 启动时自动批量创建引擎 + 存储实例。
3.1 MVEL 引擎 + 文件存储
easy:
engines:
instances:
# 价格计算规则引擎(MVEL + 文件存储)
- beanName: priceRulesEngine
type: rules
implementation: mvel
enabled: true
namespace: pricing
version: "1.0.0"
repositoryType: file # in-memory | file | redis
properties:
engine: mvel
# 路由规则引擎(MVEL + Redis 存储,可选)
- beanName: routeRulesEngine
type: rules
implementation: mvel
enabled: true
namespace: routing
version: "2.0.0"
repositoryType: redis
# 独立存储实例(可选;instances 会自动创建 ConfigStorage,
# 这里用于预定义可复用的存储 Bean)
storages:
- beanName: sharedRulesStorage
repositoryType: file
properties:
basePath: /opt/rules/
3.2 LiteFlow 引擎
easy:
engines:
instances:
- beanName: flowRulesEngine
type: rules
implementation: liteflow
enabled: true
namespace: flows
# LiteFlow 自己的配置(独立 YAML 前缀)
liteflow:
rule-ext: xml
slot: c
print-execution-log: true
print-performance-log: true
sources:
- path: classpath*:liteflow/*.xml
3.3 配置项说明
instance 级别:
| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
beanName | ✅ | — | 引擎 Bean 名称(@Qualifier 注入用) |
type | ✅ | — | 目前只用 rules;state-machine / workflow 预留 |
implementation | ✅ | — | mvel / liteflow |
enabled | ❌ | true | 是否启用 |
namespace | ✅ | — | 规则命名空间(ConfigStorage 维度) |
version | ❌ | "1.0.0" | 规则版本 |
repositoryType | ✅ | — | in-memory / file / redis |
repositoryBeanName | ❌ | — | 指定已有 ConfigRepository Bean 名称(可选) |
properties | ❌ | — | 引擎特定配置 |
4. 规则定义与存储
4.1 MVEL 引擎规则格式(JSON)
规则文本通过 easy-store ConfigStorage 持久化,text 字段存 JSON 格式:
{
"rules": [
{
"name": "highPriceOrder",
"condition": "req.price > 10000",
"actions": ["resp.level = 'VIP'"],
"priority": 1
},
{
"name": "lowStockAlert",
"condition": "req.stock < 10",
"actions": ["resp.warning = 'low_stock'"],
"priority": 2
}
]
}
Facts 映射(规则脚本中可用变量):
| Easy-Rules Facts key | RulesFacts 字段 | 说明 |
|---|---|---|
head | header | 请求头 Map(只读) |
req | request | 请求体对象(只读) |
resp | result | 执行结果(规则可读写) |
util | 内置 Util | format/template, vars) / copy(from, to, keys...) / move(from, to, fromKey, toKey) |
| context entries | 各自 key | 用户传入的 context Map |
Facts 变量示例:
// 在 actions 里可以这样写:
resp.price = req.price * 1.1;
if (req.type == 'VIP') {
resp.discount = 0.9;
}
util.copy(req, resp, "category", "brand");
4.2 规则持久化(走 easy-store)
@Service
public class RuleService {
@Autowired
@Qualifier("priceRulesEngine")
private MVELRuleExecutor priceRulesEngine; // 实际是 RulesExecutor<Rules>
public void saveRule(String chainId, String ruleJson) {
ConfigDefinition def = new ConfigDefinition();
def.setNamespace("pricing");
def.setKey(chainId);
def.setText(ruleJson);
def.setEngine("mvel");
def.setVersion("2.0.0");
priceRulesEngine.saveConfig("pricing", chainId, def);
}
}
4.3 LiteFlow 规则格式(XML)
LiteFlow 规则不走 easy-store,放 classpath 下的 XML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<chain name="orderApproval">
<A/> <!-- 节点:校验请求 -->
<B/> <!-- 节点:查库存 -->
<C IF="req.amount > 10000">
<D/> <!-- 大订单额外审批 -->
</C>
<E/> <!-- 节点:生成审批单 -->
</chain>
5. 编程式 API
5.1 执行规则
@Service
public class PriceService {
@Autowired
@Qualifier("priceRulesEngine")
private RulesExecutor priceRulesEngine; // MVELRuleExecutor
public PricedOrder calculate(Order order) {
// 最简入口
Result<PricedOrder> result = priceRulesEngine.execute("priceCalcChain", order);
if (result.success()) {
return result.getData();
} else {
throw result.getException();
}
}
// 完整入口(带 header + context)
public PricedOrder calculateWithHeader(Map<String, Object> header, Order order, Map<String, Object> ctx) {
return priceRulesEngine.evaluate("priceCalcChain", header, order, ctx);
// evaluate 失败直接抛 RulesException
}
}
5.2 检查规则(不执行)
Map<String, Boolean> checkResult = priceRulesEngine.check("priceCalcChain",
RulesFacts.of(priceRequest));
// 返回每条规则名 → 是否满足条件
// {"highPriceOrder": true, "lowStockAlert": false}
boolean shouldRun = priceRulesEngine.shouldBeEvaluate("priceCalcChain",
RulesFacts.of(priceRequest));
5.3 LiteFlow 引擎使用
LiteFlow 走同样的 RulesExecutor 门面,但内部调的是 FlowExecutor.execute2Resp():
@Autowired
@Qualifier("flowRulesEngine")
private RulesExecutor flowRulesEngine;
public ApprovalResult approve(Request req) {
// 接口完全一样!业务层不感知引擎差异
return flowRulesEngine.evaluate("orderApproval", null, req, Map.of());
}
6. 动态 Bean 注册
EngineRegistryPostProcessor 在 Spring Boot 启动时根据配置批量创建引擎 + 存储 Bean:
flowchart LR
A["Spring Boot 启动<br/>BeanDefinitionRegistryPostProcessor"] --> B["Binder.bind<br/>(easy.engines)"]
B --> C["遍历 storages → 注册 ConfigStorage Bean"]
C --> D["遍历 instances → determineEngineClass<br/>+ determineRepositoryClass"]
D --> E["createConfigStorageBean<br/>(DefaultConfigStorage + Repository)"]
E --> F["创建引擎 BeanDefinition<br/>注入 configStorage + namespace"]
F --> G["Spring 实例化 + 依赖注入<br/>引擎 Bean 就绪"]
引擎类选择:
| implementation | → 类 |
|---|---|
mvel | MVELRuleExecutor |
liteflow | LiteFlowExecutor |
| 其他 | null(跳过) |
存储类选择:
| repositoryType | → 类 |
|---|---|
in-memory / memory | InMemoryRepository |
file | FileRepository |
redis | RedisRepository(半成品) |
7. MVEL vs LiteFlow 差异
| 维度 | MVELRuleExecutor | LiteFlowExecutor |
|---|---|---|
| 成熟度 | 完整生产级 | 可用 |
| 规则存储 | easy-store ConfigStorage | LiteFlow 自己的 XML/脚本 |
| 规则格式 | JSON(Easy-Rules 定义) | XML(LiteFlow 链路 DSL) |
| 执行方式 | Easy-Rules DefaultRulesEngine.fire() | LiteFlow FlowExecutor.execute2Resp() |
| Facts key | head/req/resp + util | request + context beans |
| 链式编排 | 规则按 priority 排序执行 | LiteFlow XML 链路(IF/THEN/ELSE/循环/条件) |
| 规则热加载 | ConfigStorage.saveConfig() 后需重启 | LiteFlow 支持 IFlowExecutor.reload() |
| createRules | ConfigDefinition.text → MVELRulesLoader | throw UnsupportedOperationException |
| 内置 Util | format/copy/move | 无(用 LiteFlow 组件) |
8. 常见问题
Q: MVEL 规则改了怎么生效? A: 当前版本不支持热加载。ConfigStorage.saveConfig() 之后需要重启应用。规则存储 ConfigDefinition 在 ConfigStorage.cache 里只在首次 load 时填充,改 text 不更新缓存。
Q: LiteFlow 怎么桥接 easy-store? A: 目前不可行。LiteFlow 的 createRules 抛 UnsupportedOperationException,它走自己的规则加载机制(XML/脚本文件),不走 ConfigStorage。如果一定要统一,得自己实现 LiteFlow 的 IFlowExecutor 子类。
Q: JEXL 能用吗? A: 不行。createRules() 抛 UnsupportedOperationException,EngineRegistryPostProcessor 也没 "jexl" 分支。依赖声明在 pom.xml 里但用不了,属于预留。
Q: 多个引擎实例可以共存吗? A: 可以。EngineRegistryPostProcessor 会批量创建所有 enabled 的 instance,每个有独立 beanName 和 namespace。
Q: ConfigStorage.loadAttachment 是干嘛的? A: RulesExecutor 通过它懒加载规则——首次 execute 时从 ConfigStorage loadConfig → createRules 转成引擎特定 Rules 对象 → 存入 ConfigDefinition.attachment。后续执行直接用 attachment,避免重复转换。
Q: EngineRegistryPostProcessor 和 easy-store 的关系? A: 它自己不引入 easy-store 的 @EnableConfigurationProperties——但 EngineExecutor 顶层基类持有 ConfigStorage 字段,而 EngineRegistryPostProcessor 创建引擎 Bean 时会注入 ConfigStorage。依赖通过 easy-rules pom.xml 的 compile scope 声明的 easy-store 传递引入。
Q: RedisRepository 能用吗? A: 有限能用。单 key 的 save/load/delete 可以,listKeys / delete(namespace) 不可用(半成品)。建议生产用 FileRepository 或 InMemoryRepository。