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

相关推荐
沈询-阿里32 分钟前
java-智能识别车牌号_基于spring ai和开源国产大模型_qwen vl
java·开发语言
AaVictory.38 分钟前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
LuckyLay1 小时前
Spring学习笔记_27——@EnableLoadTimeWeaving
java·spring boot·spring
向阳12181 小时前
Dubbo负载均衡
java·运维·负载均衡·dubbo
Gu Gu Study1 小时前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
WaaTong2 小时前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
m0_743048442 小时前
初识Java EE和Spring Boot
java·java-ee
AskHarries2 小时前
Java字节码增强库ByteBuddy
java·后端
小灰灰__2 小时前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭2 小时前
Java中的动态代理
java·开发语言·aop·动态代理