一佳技术

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 keyRulesFacts 字段说明
headheader请求头 Map(只读)
reqrequest请求体对象(只读)
respresult执行结果(规则可读写)
util内置 Utilformat/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→ 类
mvelMVELRuleExecutor
liteflowLiteFlowExecutor
其他null(跳过)

存储类选择:

repositoryType→ 类
in-memory / memoryInMemoryRepository
fileFileRepository
redisRedisRepository(半成品)

7. MVEL vs LiteFlow 差异

维度MVELRuleExecutorLiteFlowExecutor
成熟度完整生产级可用
规则存储easy-store ConfigStorageLiteFlow 自己的 XML/脚本
规则格式JSON(Easy-Rules 定义)XML(LiteFlow 链路 DSL)
执行方式Easy-Rules DefaultRulesEngine.fire()LiteFlow FlowExecutor.execute2Resp()
Facts keyhead/req/resp + utilrequest + context beans
链式编排规则按 priority 排序执行LiteFlow XML 链路(IF/THEN/ELSE/循环/条件)
规则热加载ConfigStorage.saveConfig() 后需重启LiteFlow 支持 IFlowExecutor.reload()
createRulesConfigDefinition.text → MVELRulesLoaderthrow UnsupportedOperationException
内置 Utilformat/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。