规则引擎easy-rule快速入门及整合SpEL使用

news2024/11/24 4:12:10

规则引擎介绍

规则引擎解决的实际上就是判断条件分支过多的问题,举个例子,营销策略里,消费不足200,可用10元消费券,消费超过200元,可以享受9折优惠,超过400元和享受8折优惠。这里可以直接用if else来判断,因为规则比较少,而当判断条件十分复杂之后,使用规则引擎就会能提高系统的可维护性,也能够提高可扩展性。
本文主要介绍规则引擎easy-rule的基本概念和基本使用,同时也会包含使用SpEL表达式作为条件和操作的例子。

基本概念

name:规则命名
description:规则描述
priority:规则的优先级,数字越小,优先级越高
facts:触发规则时的一组已知事实,底层是一个包含多个类似于哈希表fact的set。
conditions:需要满足的条件
actions:满足条件执行的操作
@Rule:定义规则,包括条件(@Condition)和操作(@Action)
RuleBuilder:通过链式结构构件规则

需求实现

我们就以一开始营销的例子,用几种不同的方式实现。

首先引入相关依赖

        <dependency>
            <groupId>org.jeasy</groupId>
            <artifactId>easy-rules-core</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.jeasy</groupId>
            <artifactId>easy-rules-mvel</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.jeasy</groupId>
            <artifactId>easy-rules-support</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.jeasy</groupId>
            <artifactId>easy-rules-spel</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.2.5.RELEASE</version>

注解实现

定义了价格小于200时的规则

//该注解表示规则
@Rule
public class CutTenRule {

    private Integer price;

    // 该注解标记为条件
    @Condition
    public boolean isLowerThan200(@Fact("price") Integer price) {
        this.price = price;
        return price < 200;
    }

    // 该注解表示条件判断之后的操作
    @Action
    public void printPriceAfterDiscount() {
        System.out.println("折扣规则为减10元\n原价为:"+price+"\n折扣后价格的价格为:" + (price > 10 ? price - 10 : price));
    }

    //该注解表示优先级
    @Priority
    public int getPriority() {
        return 0;
    }

}

用接口实现

定义了价格在200~400之间的折扣规则

// 通过接口的方式标记为条件
public class TenPercentDiscountCondition implements Condition {

    @Override
    public boolean evaluate(Facts facts) {
        Integer price = (Integer) facts.get("price");
        return price >= 200 && price < 400;
    }
    
}

// 通过接口方式标记为条件运行之后的操作
public class TenPercentDiscountAction implements Action {
    @Override
    public void execute(Facts facts) throws Exception {
        Integer price = facts.get("price");
        System.out.println("折扣规则为9折\n原价为:"+price+"\n折扣后的价格为:" + (float)(price * 0.9));
    }
}

规则引擎执行

这样我们就实现了两种方式,接下来就是封装这两个规则。
首先,我们介绍一下规则引擎的几个参数。

规则引擎可配置的参数:

  • skipOnFirstAppliedRule:当一个规则成功应用时,跳过余下的规则。
  • skipOnFirstFailedRule:当一个规则失败时,跳过余下的规则。
  • skipOnFirstNonTriggeredRule:当一个规则未触发时,跳过余下的规则。
  • rulePriorityThreshold:当优先级超过指定的阈值时,跳过余下的规则。

规则引擎的构造方法:

  • DefaultRulesEngine:根据规则的自然顺序(默认为优先级)应用规则。
  • InferenceRulesEngine:在已知的事实上不断地应用规则,直到没有更多的规则可用。
public class DiscountClient {
    public static void main(String[] args) {
        // 创建执行器参数
        RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
        // 创建执行期引擎
        DefaultRulesEngine discountEngine = new DefaultRulesEngine(parameters);
        // 链式规则完成接口规则的定义
        Rule tenPercentRule = new RuleBuilder().name("200~400的折扣")
                .description("200~400的折扣")
                .when(new TenPercentDiscountCondition())
                .then(new TenPercentDiscountAction())
                .build();
        // 注解实现方式
        CutTenRule cutTenRule = new CutTenRule();
        // 添加规则
        Rules rules = new Rules();
        rules.register(tenPercentRule);
        rules.register(cutTenRule);
        // 定义事实
        Facts facts = new Facts();
        for (int i = 0; i < 400; i += 9) {
            // 添加事实
            facts.put("price", i);
            // 触发引擎
            discountEngine.fire(rules, facts);
        }
    }
}

运行结果
在这里插入图片描述

组合规则

除了自己添加规则之后,easy-rule还提供了组合规则

  • UnitRuleGroup:单元规则组是作为一个单元使用的组合规则,要么应用所有规则,要么不应用任何规则。
  • ActivationRuleGroup:激活规则组触发第一个适用规则并忽略组中的其他规则。规则首先按照其在组中的自然顺序(默认情况下优先级)进行排序。
  • ConditionalRuleGroup:条件规则组将具有最高优先级的规则作为条件,如果具有最高优先级的规则的计算结果为true,那么将触发其余的规则。

以UnitRuleGroup为例:

		UnitRuleGroup unitRuleGroup = new UnitRuleGroup("单元规则", "单元规则");
        unitRuleGroup.addRule(cutTenRule);
        unitRuleGroup.addRule(tenPercentRule);
        // 添加规则
        Rules rules = new Rules();
        rules.register(unitRuleGroup);

按此规则运行上面的引擎,没有任何输出
将之前的规则200这个临界点两个都适用
可以看到在200的地方都执行了
在这里插入图片描述

监听器

easy-rule还提供了监听器的的功能,在条件执行前等位置都可以提供监听,只要实现监听器接口即可,这里代码见名知义,不再赘述。

public class MyRuleListener implements RuleListener {
    @Override
    public boolean beforeEvaluate(Rule rule, Facts facts) {
        return RuleListener.super.beforeEvaluate(rule, facts);
    }

    @Override
    public void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) {
//        RuleListener.super.afterEvaluate(rule, facts, evaluationResult);
        System.out.println("condition评估之后");
    }

    @Override
    public void beforeExecute(Rule rule, Facts facts) {
        System.out.println("action执行之前");
//        RuleListener.super.beforeExecute(rule, facts);
    }

    @Override
    public void onSuccess(Rule rule, Facts facts) {
        System.out.println("action执行成功");
//        RuleListener.super.onSuccess(rule, facts);
    }

    @Override
    public void onFailure(Rule rule, Facts facts, Exception exception) {
        System.out.println("action执行失败");
//        RuleListener.super.onFailure(rule, facts, exception);
    }
}

只需要在执行器中添加即可

        // 添加监听器
        discountEngine.registerRuleListener(new MyRuleListener());

执行效果:
在这里插入图片描述

使用Spel

我们以对象为例实现上述200~400的情况
首先创建商品对象

@Data
@AllArgsConstructor
public class Item {
    private int price;
    private String expression;

    public Item(int price) {
        this.price = price;
    }
}

创建spel规则

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

在rules中注册

rules.register(spelRule);

测试结果

            facts.put("item", item);
            // 触发引擎
            discountEngine.fire(rules, facts);
            System.out.println(item.getExpression());

运行结果
在这里插入图片描述
执行引擎完整代码:

public class DiscountClient {
    public static void main(String[] args) {
        // 创建执行器参数
        RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
        // 创建执行期引擎
        DefaultRulesEngine discountEngine = new DefaultRulesEngine(parameters);
        // 添加监听器
        discountEngine.registerRuleListener(new MyRuleListener());
        // 链式规则完成接口规则的定义
        Rule tenPercentRule = new RuleBuilder().name("200~400的折扣")
                .description("200~400的折扣")
                .when(new TenPercentDiscountCondition())
                .then(new TenPercentDiscountAction())
                .build();
        //spe方式
        SpELRule spelRule = new SpELRule().name("1")
                .description("1")
                .priority(1)
                .when("#{ ['item'].price >= 400 }")
                .then("#{ ['item'].setExpression('折扣规则为8折\n原价为:' + ['item'].price " +
                        "+ '\n折扣后的价格为:'" +
                        " + T(java.lang.Float).parseFloat(['item'].price * 0.8+'') )  }");
        // 注解实现方式
        CutTenRule cutTenRule = new CutTenRule();
        // 创建组合规则
        UnitRuleGroup unitRuleGroup = new UnitRuleGroup("单元规则", "单元规则");
        unitRuleGroup.addRule(cutTenRule);
        unitRuleGroup.addRule(tenPercentRule);
        // 添加规则
        Rules rules = new Rules();
        rules.register(unitRuleGroup);
        rules.register(spelRule);
        // 定义事实
        Facts facts = new Facts();
        for (int i = 0; i < 500; i += 10) {
            // 添加事实
            facts.put("price", i);
            Item item = new Item(i);
            facts.put("item", item);
            // 触发引擎
            discountEngine.fire(rules, facts);
            System.out.println(item.getExpression());
        }
    }
}

总结

执行引擎easy-rule具有强大的解决分支问题的能力,在有多个判断条件时能够使代码的复用性和扩展性提高,同时支持spel等多种表达式,本文主要介绍了简单使用,希望能帮到你~

参考资料

https://juejin.cn/post/7048917724126248967
https://github.com/j-easy/easy-rules
https://zhuanlan.zhihu.com/p/93431125

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/157674.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【houdini】带着uv进行模拟(冰激凌案例)

整个sop的路线图 前半部分&#xff1a;赋予材质并分离冰激凌球 一般模型是分部分的&#xff0c;比方说下面这个冰激凌的&#xff0c;分为5个部分。三个不同的冰激凌球和水和杯子。这些部分在houdini中被称为group。 用material节点一口气赋予5个group材质。 具体的材质的指…

【Linux】 gcc 、动态库和静态库,程序是如何链接的

文章目录前言一、gcc 是什么&#xff1f;二、使用步骤1.预编译2.编译3.汇编4.链接三、动静态库1.概念2.区别前言 在Linux环境下&#xff0c;除了学好编辑器 vim 的使用&#xff0c;还需要学会C语言的编译器 gcc 的功能&#xff0c;否则代码无法翻译成可执行程序。本文将介绍 gc…

怎样快速地迁移 MySQL 中的数据?

我们通常会遇到这样的一个场景&#xff0c;就是需要将一个数据库的数据迁移到一个性能更加强悍的数据库服务器上。这个时候需要我们做的就是快速迁移数据库的数据。那么&#xff0c;如何才能快速地迁移数据库中的数据呢&#xff1f;今天我们就来聊一聊这个话题。数据库的数据迁…

Spring Boot 日志详解

Spring Boot 日志一、日志有什么用二、日志怎么用三、自定义日志打印3.1 在程序中得到日志对象3.2 使用日志对象打印日志四、日志级别4.1 日志级别有什么用4.2 日志级别的分类与使用4.3 日志级别设置五、日志持久化5.1 配置文件名5.2 配置保存路径六、更简单的日志输出 -- lomb…

Go第 13 章 :2-客户信息关系系统

Go第 13 章 &#xff1a;2-客户信息关系系统 13.1 项目需求分析 模拟实现基于文本界面的《客户信息管理软件》。该软件能够实现对客户对象的插入、修改和删除&#xff08;用切片实现&#xff09;&#xff0c;并能够打印客户明细表 13.2 项目的界面设计 主菜单界面 添加客户…

SegFormer笔记(1)安装

一、代码地址github地址&#xff1a;https://github.com/NVlabs/SegFormer二、我的系统配置&#xff1a;1、硬件2、软件win10&#xff0c;vs2022&#xff0c;python3.7三、创建虚拟环境conda create -n MySegFormer python3.7 numpy conda activate MySegFormer python会显示如…

【知识图谱工具汇总】

一&#xff0c;图表示学习与计算框架 1&#xff09;PyTorch Geometric&#xff08;PyG&#xff09; 由德国多特蒙德工业大学研究者推出的基于PyTorch的几何深度学习扩展库。PyG在学术中是比较热门的框架&#xff0c;但是PyG对于异构图以及大规模的图的学习存在着较大的局限性…

【UE4 第一人称射击游戏】39-“M4A1”武器设置

上一篇&#xff1a;【UE4 第一人称射击游戏】38-拾取“M4A1”的提示信息步骤&#xff1a;在“AK47”文件夹中拷贝一份“Weapon_Base”拷贝到“M4A1”文件夹下&#xff0c;重命名为“Weapon_M4A1”打开“Weapon_M4A1”&#xff0c;新添加一个网格体组件骨架网格体选择“M4A1_wea…

Linux编辑器-gcc/g++使用

目录 背景&#xff1a; 预处理&#xff1a; 编译&#xff1a; 汇编&#xff1a; 连接&#xff1a; 静态连接&#xff1a; 动态连接&#xff1a; 自动化编程make/makefile&#xff1a; 背景&#xff1a; 我们知道任何一个c语言或者c文件想要生成一个可执行程序必须完成4个…

NotepadAttr

EntityMyPKAttr目录概述需求&#xff1a;设计思路实现思路分析1.Notepads2.EntityMyPK3.EntityMyPKAttr4.EntitiesMyPK5.EntityMyPKEntityMyPKAttrEntitiesMyPKWebContralBaseAtParaCashChartType参考资料和推荐阅读Survive by day and develop by night. talk for import biz …

JS面试题--JS函数式编程

JS函数式编程 实现apply、call、bind 01_call函数的实现 // apply/call/bind的用法 // js模拟它们的实现? 难度// 给所有的函数添加一个hycall的方法 同个原型链 Function.prototype.hycall function (thisArg, ...args) {// 在这里可以去执行调用的那个函数(foo)// 问题: …

亚马逊云科技:“云”筹帷幄,打造数据驱动型企业

数据对提高企业生产和资源配置的效率、优化经济结构的作用日益凸显。工业和信息化部发布的《“十四五”大数据产业发展规划》已明确将数据作为新时代重要的生产要素和国家基础性战略资源。埃森哲研究显示&#xff0c;到2022年&#xff0c;90%的企业战略明确将把数据作为关键的企…

前端面试题回顾——React重要知识

文章目录React相关问题1. 函数式组件与Class组件有什么不同&#xff1f;2. 说说React的fiber架构&#xff1f;3. 协调4. 虚拟DOM (Virtual DOM)React相关问题 1. 函数式组件与Class组件有什么不同&#xff1f; 答&#xff1a; ① 函数式组件不需要继承&#xff0c;直接 “fun…

数据结构基础——认识数据结构与算法

目录 &#x1f354;什么是数据结构&#xff1f; &#x1f32d;1.数据的逻辑结构 &#x1f32d;NUM 1 : 集合 &#x1f32d;NUM 2 : 线性结构 &#x1f32d;NUM 2 : 树形结构 &#x1f32d;NUM 4 :图结构&#xff08;网状结构&#xff09;…

浅谈反弹shell

目录反弹shell总结一、文件描述符二、重定向1、输入重定向2、输出重定向3、错误输出重定向4、exec 绑定重定向三、实现反弹shell的几种方式方法一&#xff1a; find 反弹ubuntu find反弹shell失败的问题任务计划反弹shell失败的问题方法二&#xff1a;使用python反弹&#xff0…

Flink系列-4、Flink运行架构

版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 大数据系列文章目录 官方网址&#xff1a;https://flink.apache.org/ 学习资料&#xff1a;https://flink-learning.org.cn/ 目录Flink基石Fli…

刚当上leader,我让组员去开会,他非说有更重要的会

☆ 职场上经常有那么一种情况就是组长喊组员开会&#xff0c;开周会&#xff0c;开晨会&#xff0c;开各种会&#xff0c;而更有一种常见的情况呢就是组长缺失威严&#xff0c;喊组员开会&#xff0c;组员不听话&#xff0c;说有更重要的会议&#xff0c;不想参加。 ☆ 本文将以…

VIT学习心得

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 小声逼逼 在过去的两年里&#xff0c;Vision Transformer(ViT)是计算机视觉(cv)领域最有影响力的工作之一。「它推翻了2012年在Alex net中提出的CNN在CV领域的统治地位&#xff1a; 当能够获得足够多的预训练…

无接触式磁旋转编码器AS5040介绍

无接触式磁旋转编码器AS5040简介AS5040 是一款无接触式磁旋转编码器&#xff0c;用于精确测量整个360内的角度。此产品是一个片上系统&#xff0c;在单个封装内整合了集成式Hall 元件、模拟前端和数据信号处理功能。测量角度时&#xff0c;只需简单地配备1 个在芯片中心上方旋转…

Spring5的全细节回顾总结

概述&#xff1a; https://cntofu.com/book/95/33-what-new-in-the-spring-framework.md 这个不错。 轻量级javaee框架。 针对于bean的生命周期进行管理。 解决企业应用开发的复杂性。 核心&#xff1a; ​ IOC&#xff1a;控制反转&#xff0c;把创建对象的过程交给sprin…