基于Aviator开发一个简单的规则引擎

新需求,针对众包平台的订单信息录入增加一些通用的规则校验。

重构理由

1.不同端的订单历史规则分散在代码里,对业务侵入严重,不利于修改
2.为了通用的规则重用
3.后续业务的计算逻辑独立,可通过规则引擎的方式进行计算

目标

1.为了业务、校验、计算的分离
2.降低耦合、进行统一的规则管理
3.增强拓展性,后续仅需对规则配置即可


考虑使用Google的Aviator框架来实现这个需求

Aviator是什么

Aviator是一个高性能的、轻量级的Java表达式求值引擎,它支持Java语法和Aviator语法。
Aviator语法是一种简化的表达式语法,它支持类似Python的三元表达式、正则表达式、集合操作等语法糖,使得表达式更加简洁易读。

有需要的xdm可以看下下面这篇文章,非常详细地介绍了Aviator的语法、函数、使用以及进阶玩法等。
对新手比较友好。
超级详细的Aviator的使用文档

为什么选用Aviator

Aviator底层是读取表达式动态实时地编译成JVM字节码,大伙应该很清除这意味着高性能
使用简单,解耦好,轻量级
拓展性好,可以自定义函数,另外它的内置函数比较完善,很强大,可以在使用时对照着上述文档使用
当然最重要的一个原因是我之前使用过它

1.引入依赖

我可是很贴心的中央空调,必须给大伙儿将依赖贴出来

Gradle依赖
implementation 'com.googlecode.aviator:aviator:5.3.3'
Maven依赖
<dependency>
  <groupId>com.googlecode.aviator</groupId>
  <artifactId>aviator</artifactId>
  <version>5.3.3</version>
</dependency>
2.代码结构

在这里插入图片描述
具体规则配置,可以看项目或者个人喜好,Json、XML、Yaml都行,怎么觉得方便怎么来
在这里插入图片描述
我配置的解析实例如下,自定义即可,看自己业务场景。

[
    {
        "ruleType": "Test",
        "ruleName": "orgManager",
        "ruleExpression": "orgManager(userName, companyName)",
        "hitDesc": "规则1"
    },
    {
        "ruleType": "Test",
        "ruleName": "recentTwoMonthSameMoney",
        "ruleExpression": "recentTwoMonthSameMoney(entity)",
        "hitDesc": "规则2"
    }
]
核心逻辑执行器
@Component
@Slf4j
public class RuleEngineCmdExe {

    private static List<Rule> rules;

    public RuleExecuteResult executeRule(RuleContext ruleContext) {
        //init param
        RuleExecuteResult result = new RuleExecuteResult();
        result.setResultType(RuleExecuteResultEnum.PASS);
        List<String> hitDesc = new ArrayList<>();

        if (CollUtil.isEmpty(ruleContext.getParam())) {
            throw new ContractBaseException(ResponseCodeEnum.DATA_NOT_EXIST);
        }

        Map<String, Object> map = ruleContext.getParam();
        if (ObjectUtil.isNotEmpty(ruleContext.getData())) {
            map.put("entity", ruleContext.getData());
        }

        //循环执行每条逻辑,并组装返回结果
        for (Rule rule : rules) {
            boolean hit = (boolean) AviatorEvaluator.execute(rule.getRuleExpression(), map, true);
            if (hit) {
                log.info("hit rule: {}", rule.getHitDesc());
                result.setResultType(RuleExecuteResultEnum.REFUSE);
                hitDesc.add(rule.getHitDesc());
            }
        }
        result.setHitRules(hitDesc);
        return result;
    }

    @PostConstruct
    public void init() {
        try {
            File file = ResourceUtils.getFile("classpath:rule.json");
            rules = JSONArray.parseArray(FileUtils.readFileToString(file, "UTF-8"), Rule.class);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

自定义函数

我上述截图中的自定义函数由于内部业务问题,就不展示了,贴个Demo吧
使用自定义函数,根据getName()返回的函数名(可自定义),直接在上述的rule.json文件的规则表达式配置name,进行使用

public class CustomFunction extends AbstractFunction {
    @Override
    public AviatorObject call(Map<String, Object> env) {
        double x = (double) env.get("x");
        double y = (double) env.get("y");
        return new AviatorDouble(x + y);
    }

    @Override
    public String getName() {
        return "add";
    }
}
3.执行流程

在这里插入图片描述

  1. 确定业务调用点
  2. 使用RuleContext包装参数和数据
  3. 使用RuleEngineCmdExe,传入RuleContext
  4. AviatorEvaluator执行已解析的rule.json,rule.json在系统初始化的时候解析一次即可
  5. 返回结果

如果需要新增函数,主要自定义一个类继承AbstractFunction
继承call方法,编写函数逻辑。

如果需要新增规则,只要在rule.json中进行配置即可。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值