easy-rule规则引擎使用

简介

轻量级的规则引擎,易于学习的api

简单来说,规则引擎就是一个函数:y=f(x1,x2,...,xn)

将业务代码和业务规则分离,解耦业务决策和业务代码的绑定关系

入门示例

依赖引入
java 复制代码
<dependency>
     <groupId>org.jeasy</groupId>
     <artifactId>easy-rules-core</artifactId>
     <version>4.1.0</version>
</dependency>

<!--规则定义文件格式,支持json,yaml等-->
<dependency>
  <groupId>org.jeasy</groupId>
  <artifactId>easy-rules-support</artifactId>
  <version>4.1.0</version>
</dependency>

<!--支持mvel规则语法库-->
<dependency>
  <groupId>org.jeasy</groupId>
  <artifactId>easy-rules-mvel</artifactId>
  <version>4.1.0</version>
</dependency>

<dependency>
  <groupId>org.jeasy</groupId>
  <artifactId>easy-rules-spel</artifactId>
  <version>4.1.0</version>
</dependency>
代码示例
java 复制代码
public static void main(String[] args) {

        Rules rules = new Rules();

        Rule rule = new RuleBuilder()
                .name("test")
                .description("测试").priority(3)
                .when(facts -> facts.get("userName").equals("zhangsan"))
                .then(facts -> System.out.println("this is test action!"))
                .build();

        rules.register(rule);

        Facts facts = new Facts();
        facts.put("userName","zhangsan");

        DefaultRulesEngine defaultRulesEngine = new DefaultRulesEngine();
        defaultRulesEngine.fire(rules,facts);
    }
核心概念
规则(Rule)

由条件和行动构成的推理语句,通俗的讲:当满足某个条件之后,需要做后续的动作(一个或多个动作),一般表示为IF <conditions> THEN <actions>, Rule表达逻辑。一个规则的IF部分称为LHS,THEN部分称为RHS。

除了以上示例方式构建Rule,还可以通过注解构建Rule:

java 复制代码
@Rule(name = "repeatRule",description = "重复规则对消息内容去重",priority = 100)
public class RepeatRule {

    @Condition
    public boolean evaluate(@Fact("sendMessages") Set<String> sendMessages, @Fact("message") String message) {
        return sendMessages.contains(message);
    }

    @Action(order =0)
    public void execute(Facts facts) {
        //
    }

    @Action(order = 1)
    public void breakRules(Facts facts) {
        facts.put("break",true);
        facts.put("reason","repeat limit");
    }

}
事实(Facts)

一组用户的输入数据,规则将根据输入数据判断是否满足条件,从而触发规则actions

Facts是一组Fact集合,源码示例如下:

而Fact是一个容器,可以简单的理解成一个Map对象:

执行引擎(RulesEngine)

引擎执行器,一般为推理引擎。Rules使用LHS与事实进行模式匹配。当匹配被找到,Rules会执行RHS即执行逻辑,同时actions经常会改变facts的状态,来推理出期望的结果。

常用的执行引擎即:DefaultRulesEngine

结合mvel使用示例

java 复制代码
@Test
    public void mvelExecute() {
        MVELRule repeatRule = new MVELRule().name("repeat Rule")
                .priority(100)
                .when("!sendMessages.contains(msgId)")
                .then("result.put('break',true);result.put('reason','repeat limit')");

        Rules rules = new Rules(repeatRule);

        Facts facts = new Facts();

        facts.put("msgId","123456");
        facts.put("sendMessages",new TreeSet<>());
        facts.put("msgType","interaction");
        facts.put("message","hello world");
        facts.put("result",new HashMap<>());

        DefaultRulesEngine engine = new DefaultRulesEngine();
        //CustomRuleListener customRuleListener = new CustomRuleListener();
        //engine.registerRuleListener(customRuleListener);
        engine.fire(rules,facts);
        Map<String,Object> result = (Map<String,Object>)facts.get("result");

        log.info("执行结果是:{}", JSONUtil.toJsonStr(result));
    }

MVEL可以支持表达式支持常用的逻辑符:> = < && || 等,还支持java类型api的调用

最直观的使用MVEL表达是API如下:

java 复制代码
 @Test
    public void mvelCompileTest(){
        MyTest obj = new MyTest();
        Map<String, Object> map = new HashMap<>();
        map.put("key1", 1);
        map.put("key2", 2);

        String expression = "func(map)";
        Map<String, Object> vars = new HashMap<>();
        vars.put("map", map);

        MVEL.executeExpression(MVEL.compileExpression(expression), obj, vars);
        System.out.println(map);

    }

    public void func(Map<String, Object> map) {
        map.put("test","lisi");
    }

详细的MVEL脚本语言的使用:http://mvel.documentnode.com/

结合spel使用示例

java 复制代码
public static void main(String[] args) {

        Rules rules = new Rules();

        SpELRule spelRule = new SpELRule().name("1")
                .description("1")
                .priority(1)
                .when("#{ ['item'].price >= 100 }")
                .then("#{ ['item'].setExpression('折扣1折\n原价为:' + ['item'].price " +
                        "+ '\n折扣后的价格为:'" +
                        " + T(java.lang.Float).parseFloat(['item'].price * 0.1+'') )  }");

        rules.register(spelRule);
        Item item = new Item(500);
        Facts facts = new Facts();
        facts.put("item", item);

        DefaultRulesEngine defaultRulesEngine = new DefaultRulesEngine();
        // 触发引擎
        defaultRulesEngine.fire(rules, facts);
        System.out.println(item.getExpression());

    }

企业级应用

如果规则引擎的使用仅仅只如以上硬编码的使用方式,还不足以体现规则引擎的强大。试看以下代码:

java 复制代码
@Test
    public void factoryTest() throws Exception {

        ClassPathResource classPathResource = new ClassPathResource("rule.yml");
        String absolutePath = classPathResource.getAbsolutePath();

        YamlRuleDefinitionReader ymlReader = new YamlRuleDefinitionReader();
        MVELRuleFactory mvelRuleFactory = new MVELRuleFactory(ymlReader);
        Rules rules = mvelRuleFactory.createRules(new FileReader(absolutePath));

        DefaultRulesEngine rulesEngine = new DefaultRulesEngine();
        JSONObject entries = new JSONObject();
        entries.set("productId","1");
        entries.set("type","1");

        Facts facts = new Facts();
        facts.put("canteen", entries);

        rulesEngine.fire(rules, facts);
    }

yml内容如下:

XML 复制代码
---
name: "规则1"
description: "prouductId = 1 && type = 1 "
condition: "canteen.productId==1&&canteen.type==1"
priority: 1
actions:
  - "com.byd.performance.easyruledemo.four.UserInfoService.getNowTime();"
---
name: "规则2"
description: "prouductId = 1 && type = 2"
condition: "canteen.productId == 2 && canteen.type == 1"
priority: 2
actions:
  - "System.out.println(2);"

以上通过配置文件定义规则,将规则的触发决策通过文本的方式表述,解耦了业务逻辑代码和规则决策,大大的提升了该引擎的实用性。试想如下业务场景:

公司为了对产品经销商加强管理,需要制定一系列的处罚措施,当触犯某条规则时,将受到相应的惩罚措施。如果采用代码实现决策过程,需要大量的if else来完成,且当需要改变处罚措施时,需要修改代码,重新发布。

如果把决策过程由以上配置文件完成(再配合规则变动刷新等措施或者直接把规则数据由数据库存储加载),可以方便的支持需求变更(此处只是讨论决策的变动,如果需要新增加某种处罚措施也需要代码调整、发布)

源码浅析

easy-rule的源码较为简单(最主要是将触发规则(Condition)和执行逻辑(Action)解耦的思想),以下以简单的示例做源码分析:

org.jeasy.rules.core.DefaultRulesEngine#doFire

可以看出执行引擎的核心逻辑逻辑清晰、简单--就是通过循环Rule,判断是否命中规则,然后执行规则逻辑即可

其中执行引擎有4个参数可设置,分别释义如下:

复制代码
skipOnFirstAppliedRule:Parameter to skip next applicable rules when a rule is applied(当第一个被命中的规则执行后,是否不执行后续规则)代码line 115就是改功能的释义

skipOnFirstNonTriggeredRule:Parameter to skip next applicable rules when a rule is non triggered(当有规则的判断逻辑未命中或判断是否命中异常,是否跳过后续规则,代码line102,line130 是逻辑释义)

isSkipOnFirstFailedRule:Parameter to skip next applicable rules when a rule has failed(当执行某个规则逻辑异常时是否跳过后续规则)

priorityThreshold:规则优先级的阈值,超过该阈值的规则不被执行,默认值是Integer.MAX_VALUE

功能展望

在上面的企业级应用中有提到过,将规则决策由代码实现转而由配置文件(或者存储于数据库)中实现,解耦了规则和执行逻辑。(执行逻辑--知识库)知识库的更新需要走发布流程。如果实现以下功能是不是能更好的发挥该框架的功能

1 解藕知识库与业务逻辑的耦合性,维护知识库来进行知识与版本的管理

2 动态增加知识库,让JVM动态加载新的知识库

3 屏蔽语言壁垒,能够让运营、产品支持在线规则配置、发布是最终要实现的重要目标。做到无需研发接入,实时发布

相关推荐
微风粼粼9 分钟前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo
缘来是庄13 分钟前
设计模式之中介者模式
java·设计模式·中介者模式
rebel43 分钟前
若依框架整合 CXF 实现 WebService 改造流程(后端)
java·后端
代码的余温2 小时前
5种高效解决Maven依赖冲突的方法
java·maven
慕y2742 小时前
Java学习第十六部分——JUnit框架
java·开发语言·学习
paishishaba2 小时前
Maven
java·maven
张人玉3 小时前
C# 常量与变量
java·算法·c#
Java技术小馆3 小时前
GitDiagram如何让你的GitHub项目可视化
java·后端·面试
Codebee3 小时前
“自举开发“范式:OneCode如何用低代码重构自身工具链
java·人工智能·架构
程序无bug3 小时前
手写Spring框架
java·后端