规则引擎架构-基于easy-rules

news2024/11/17 23:27:32

目录

    • 概念理解
    • 实例和编码
      • 抽象出2条规则
      • 简单的规则引擎
      • 事实1的处理
      • 事实2的处理
    • easy-rules 规则的抽象和执行
      • 事实描述
      • 规则的抽象
        • 默认的规则
      • 动态代理执行规则和动作
        • 规则的执行:org.jeasy.rules.core.DefaultRulesEngine#doFire
        • public class RuleProxy implements InvocationHandler
        • 规则执行监听器
      • 回顾规则执行和监听器的执行过程
      • 扩展

概念理解

描述一个简单的处理:基于一堆现实情况,运用规则引擎、经过处理得到对应的结果,然后再据此做后续的事情

  • fact: 事实,已有的现实情况,即输入信息
  • rules: 规则集合,由一系列规则组成,可能有不同的规则排列
  • rule: 规则,包含基本的判断条件和条件符合要做的动作。
  • condition: 规则的判定条件(特定的判断逻辑 if else)
  • action: 规则判定符合后执行的动作
    在这里插入图片描述

实例和编码

一句话描述: 提着去酒店买酒,需要判断是否成年人,成年人才能购买酒,商店据此卖你酒,你买到了酒就装包里走人,回家喝酒去

接下来看easy-rules的定义和处理。

抽象出2条规则

@Rule(name = "age-rule", description = "age-rule", priority = 1)
public class AgeRule {

    @Condition
    public boolean isAdult(@Fact("person") Person person) {
        return person.getAge() > 18;
    }

    @Action
    public void setAdult(@Fact("person") Person person) {
        person.setAdult(true);
    }
}
package org.jeasy.rules.tutorials.shop;

import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Rule;

/**
 * @author dingqi on 2023/5/26
 * @since 1.0.0
 */
@Rule(name = "alcohol-rule", description = "alcohol-rule", priority = 2)
public class AlcoholRule {

    @Condition
    public boolean shopRule(@Fact("person") Person person) {
        return person.isAdult() == true;
    }

    @Action
    public void shopReply(@Fact("bag") Bag bag) {
        bag.setSuccess(true);
        bag.add("Vodka");
    }
}

简单的规则引擎

// create a rule set
Rules rules = new Rules();
rules.register(new AgeRule());
rules.register(new AlcoholRule());

//create a default rules engine and fire rules on known facts
DefaultRulesEngine rulesEngine = new DefaultRulesEngine();

事实1的处理

Facts facts = new Facts();
Person tom = new Person("Tom", 19);
facts.put("person", tom);
Bag bag = new Bag();
facts.put("bag", bag);

System.out.println("Tom: Hi! can I have some Vodka please?");
rulesEngine.fire(rules, facts);
System.out.println("Tom: bag is " + bag);

输出:Tom成年了,买到了伏特加

Tom: Hi! can I have some Vodka please?
Tom: bag is Bag{success=true, goods=[Vodka]}

事实2的处理

Person jack = new Person("Jack", 10);
facts.put("person", jack);
Bag bag2 = new Bag();
facts.put("bag", bag2);

System.out.println("Jack: Hi! can I have some Vodka please?");
rulesEngine.fire(rules, facts);
System.out.println("Jack: bag is " + bag2);

输出:Jack未成年,无功而返

Jack: Hi! can I have some Vodka please?
Jack: bag is Bag{success=false, goods=[]}

easy-rules 规则的抽象和执行

事实描述

public class Facts implements Iterable<Fact<?>> {

    private final Set<Fact<?>> facts = new HashSet<>();

/**
 * A class representing a named fact. Facts have unique names within a {@link Facts}
 * instance.
 * 
 * @param <T> type of the fact
 * @author Mahmoud Ben Hassine
 */
public class Fact<T> {
	
	private final String name;
	private final T value;

事实简单就是key、value对, 某个事实的名称,和事实的属性特征(以一切皆对象来看,就是一个一个的对象组成了事实)。(只要在规则条件真正执行前,能明确这些事实就行)

规则的抽象

  • 名称
  • 描述
  • 优先级
  • 执行Facts的的方法

org.jeasy.rules.api.Rule接口 和基础实现类org.jeasy.rules.core.BasicRule

条件和动作注解:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Condition {

}
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Condition {

}

默认的规则

class DefaultRule extends BasicRule {

    private final Condition condition;
    private final List<Action> actions;

    DefaultRule(String name, String description, int priority, Condition condition, List<Action> actions) {
        super(name, description, priority);
        this.condition = condition;
        this.actions = actions;
    }

    @Override
    public boolean evaluate(Facts facts) {
        return condition.evaluate(facts);
    }

    @Override
    public void execute(Facts facts) throws Exception {
        for (Action action : actions) {
            action.execute(facts);
        }
    }

}

动态代理执行规则和动作

使用org.jeasy.rules.api.Rules添加规则时如下:

  • org.jeasy.rules.api.Rules#register
 public void register(Object... rules) {
     Objects.requireNonNull(rules);
     for (Object rule : rules) {
         Objects.requireNonNull(rule);
         this.rules.add(RuleProxy.asRule(rule));
     }
 }

使用org.jeasy.rules.annotation.Rule注解构造的规则是使用RuleProxy构造的
在这里插入图片描述

规则的执行:org.jeasy.rules.core.DefaultRulesEngine#doFire

void doFire(Rules rules, Facts facts) {
    if (rules.isEmpty()) {
        LOGGER.warn("No rules registered! Nothing to apply");
        return;
    }
    logEngineParameters();
    log(rules);
    log(facts);
    LOGGER.debug("Rules evaluation started");
    for (Rule rule : rules) {
        final String name = rule.getName();
        final int priority = rule.getPriority();
        if (priority > parameters.getPriorityThreshold()) {
            LOGGER.debug("Rule priority threshold ({}) exceeded at rule '{}' with priority={}, next rules will be skipped",
                    parameters.getPriorityThreshold(), name, priority);
            break;
        }
        if (!shouldBeEvaluated(rule, facts)) {
            LOGGER.debug("Rule '{}' has been skipped before being evaluated", name);
            continue;
        }
        boolean evaluationResult = false;
        try {
            evaluationResult = rule.evaluate(facts);
        } catch (RuntimeException exception) {
            LOGGER.error("Rule '" + name + "' evaluated with error", exception);
            triggerListenersOnEvaluationError(rule, facts, exception);
            // give the option to either skip next rules on evaluation error or continue by considering the evaluation error as false
            if (parameters.isSkipOnFirstNonTriggeredRule()) {
                LOGGER.debug("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");
                break;
            }
        }
        if (evaluationResult) {
            LOGGER.debug("Rule '{}' triggered", name);
            triggerListenersAfterEvaluate(rule, facts, true);
            try {
                triggerListenersBeforeExecute(rule, facts);
                rule.execute(facts);
                LOGGER.debug("Rule '{}' performed successfully", name);
                triggerListenersOnSuccess(rule, facts);
                if (parameters.isSkipOnFirstAppliedRule()) {
                    LOGGER.debug("Next rules will be skipped since parameter skipOnFirstAppliedRule is set");
                    break;
                }
            } catch (Exception exception) {
                LOGGER.error("Rule '" + name + "' performed with error", exception);
                triggerListenersOnFailure(rule, exception, facts);
                if (parameters.isSkipOnFirstFailedRule()) {
                    LOGGER.debug("Next rules will be skipped since parameter skipOnFirstFailedRule is set");
                    break;
                }
            }
        } else {
            LOGGER.debug("Rule '{}' has been evaluated to false, it has not been executed", name);
            triggerListenersAfterEvaluate(rule, facts, false);
            if (parameters.isSkipOnFirstNonTriggeredRule()) {
                LOGGER.debug("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");
                break;
            }
        }
    }
}

默认的规则引擎直接遍历规则去执行,如果condition执行命中后,则去执行action

public class RuleProxy implements InvocationHandler

在这里插入图片描述

private Object evaluateMethod(final Object[] args) throws IllegalAccessException, InvocationTargetException {
    Facts facts = (Facts) args[0];
    Method conditionMethod = getConditionMethod();
    try {
        List<Object> actualParameters = getActualParameters(conditionMethod, facts);
        return conditionMethod.invoke(target, actualParameters.toArray()); // validated upfront
    } catch (NoSuchFactException e) {
        LOGGER.warn("Rule '{}' has been evaluated to false due to a declared but missing fact '{}' in {}",
                getTargetClass().getName(), e.getMissingFact(), facts);
        return false;
    } catch (IllegalArgumentException e) {
        LOGGER.warn("Types of injected facts in method '{}' in rule '{}' do not match parameters types",
                conditionMethod.getName(), getTargetClass().getName(), e);
        return false;
    }
}

规则执行监听器

在规则执行的过程中,可以做各种操作。可以看成规则的扩展点

/**
 * A listener for rule execution events.
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
public interface RuleListener {

    /**
     * Triggered before the evaluation of a rule.
     *
     * @param rule being evaluated
     * @param facts known before evaluating the rule
     * @return true if the rule should be evaluated, false otherwise
     */
    default boolean beforeEvaluate(Rule rule, Facts facts) {
        return true;
    }

    /**
     * Triggered after the evaluation of a rule.
     *
     * @param rule that has been evaluated
     * @param facts known after evaluating the rule
     * @param evaluationResult true if the rule evaluated to true, false otherwise
     */
    default void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) { }

    /**
     * Triggered on condition evaluation error due to any runtime exception.
     *
     * @param rule that has been evaluated
     * @param facts known while evaluating the rule
     * @param exception that happened while attempting to evaluate the condition.
     */
    default void onEvaluationError(Rule rule, Facts facts, Exception exception) { }

    /**
     * Triggered before the execution of a rule.
     *
     * @param rule the current rule
     * @param facts known facts before executing the rule
     */
    default void beforeExecute(Rule rule, Facts facts) { }

    /**
     * Triggered after a rule has been executed successfully.
     *
     * @param rule the current rule
     * @param facts known facts after executing the rule
     */
    default void onSuccess(Rule rule, Facts facts) { }

    /**
     * Triggered after a rule has failed.
     *
     * @param rule the current rule
     * @param facts known facts after executing the rule
     * @param exception the exception thrown when attempting to execute the rule
     */
    default void onFailure(Rule rule, Facts facts, Exception exception) { }

}

回顾规则执行和监听器的执行过程

// 1. 条件执行前
triggerListenersBeforeEvaluate(rule, facts);
try {
	evaluationResult = rule.evaluate(facts);
} catch(Exception e){
	 // 2. 条件执行失败
	 triggerListenersOnEvaluationError(rule, facts, exception);
}

if (evaluationResult) {
	// 3. 条件执行后(条件满足)
	triggerListenersAfterEvaluate(rule, facts, true);
	 try {
	 	  // 4. 动作执行前
	 	  triggerListenersBeforeExecute(rule, facts);
          rule.execute(facts);
          // 5. 动作执行后
          triggerListenersOnSuccess(rule, facts);
	 } catch (Exception exception) {
	 	 // 6. 条件执行失败
	 	 triggerListenersOnFailure(rule, exception, facts);
	 }
}else{
	// 3. 条件执行后(条件不满足)
	triggerListenersAfterEvaluate(rule, facts, false);
}

扩展

  1. Java Expression Language (JEXL) :表达式语言引擎

https://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/JexlEngine.html

  1. MVEL:一个功能强大的基于Java应用程序的表达式语言。

  2. SpEL:Spring表达式语言

name: adult rule
description: when age is greater than 18, then mark as adult
priority: 1
condition: "#{ ['person'].age > 18 }"
actions:
  - "#{ ['person'].setAdult(true) }"

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

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

相关文章

Java上进了,JDK21 要来了,并发编程再也不是噩梦了

更丝滑的并发编程模式 如果说之前的 JDK17你还觉得没必要折腾&#xff0c;那 JDK21确实有必要关注一下了。因为 JDK21 引入了一种新型的并发编程模式。 当前 Java 中的多线程并发编程绝对是另我们都非常头疼的一部分&#xff0c;感觉就是学起来难啃&#xff0c;用起来难用。但…

基础篇010.3 STM32驱动RC522 RFID模块之三:STM32软件模拟SPI驱动RC522

目录 1. 实验硬件及原理图 2. 利用STM32CubeMX创建MDK工程 2.1 STM32CubeMX工程创建 2.2 配置调试方式 2.3 配置时钟电路 2.4 配置时钟 2.5 配置GPIO 2.6 配置串口 2.7 项目配置 3. MDK工程驱动代码调试 3.1 按键、LED程序 3.2 SPI软件模拟程序 3.3 RC522驱动程序…

Unity制作二次元卡通渲染角色材质——1、资源分析

Unity制作二次元材质角色 回到目录 大家好&#xff0c;我是阿赵。 开始制作二次元角色材质之前&#xff0c;我觉得应该是先分析一下&#xff0c;我手上拿到的这个角色模型资源&#xff0c;总共有哪些信息是我们能用的。 所以这篇文章我不会分享具体的Shader&#xff0c;但我感觉…

基于RT-Thread快速上手SD NAND 虚拟文件系统

SD NAND 也称之为贴片式TF卡&#xff0c;贴片式SD卡&#xff0c;采用标准的SDIO接口&#xff0c;兼容SPI接口。下图所示为CS 新一代CS SD NAND NP1GCR01-AOW 大小为128M&#xff0c;对比128M的SD卡&#xff0c;可以看到贴片SD卡尺寸更小&#xff0c;不要SD卡座&#xff0c;占…

STM32杂乱笔记

问题都比较的基础和低级&#xff0c;仅记录一下。 问题一&#xff1a;stm32的某个.c文件中无法调用另一个.c中的指定变量&#xff0c;怎么解决&#xff1f; 以g_ADC_sample_vaule.Iu 为例&#xff0c;它是 drive_adc_info.c 里面的变量&#xff0c;想要在system_time_sequenc…

5.4 二叉树的性质和存储结构

博主简介&#xff1a;一个爱打游戏的计算机专业学生博主主页&#xff1a; 夏驰和徐策所属专栏&#xff1a;算法设计与分析 5.4.1 二叉树的性质 二叉树是一种特殊的树结构&#xff0c;它具有一些重要的性质&#xff1a; 1. 每个节点最多有两个子节点&#xff1a;二叉树的每个…

JavaScript:setInterval() 用法详解

文章目录 1 基本语法2 参数说明3 使用示例4 停止 setInterval() 方法 1 基本语法 setInterval() 是 JavaScript 中的一个内置函数&#xff0c;它用于在指定的间隔时间内重复执行一段代码&#xff0c;实现周期性操作。该函数的语法如下&#xff1a; setInterval(function, mil…

线程(Linux系统实现)

目录 1. 线程概述 2.主线程和子线程 3.创建线程 线程函数 创建线程示例 4.线程退出 线程退出的原理主要包括以下两个方面&#xff1a; 5.线程回收 回收子线程数据 6.线程分离 7.线程取消 8.线程 ID 比较 1. 线程概述 线程是轻量级的进程&#xff08;LWP&#xff…

【Java多线程进阶】常见的锁策略

前言 众所周知&#xff0c;拳击运动员是要分等级&#xff08;轻量级、重量级等等&#xff09;来参加比赛的&#xff0c;在 Java 多线程中 锁&#xff08;synchronized&#xff09; 也会根据锁的竞争程度来升级为相关“高等级”锁&#xff0c;为了更好的理解 synchronized 加锁机…

微信小程序node+vue医院挂号预约系统fun17

从而实现管理员后端&#xff1b;首页、个人中心、用户管理、专家管理、科室类型管理、职称类型管理、医院挂号管理、挂号信息管理、留言板管理、系统管理&#xff0c;专家后端&#xff1b;首页、个人中心、医院挂号管理、挂号信息管理、系统管理&#xff0c;用户前端&#xff1…

【Linux】网络基础+UDP网络套接字编程

只做自己喜欢做的事情&#xff0c;不被社会和时代裹挟着前进&#xff0c;是一件很奢侈的事。 文章目录 一、 网络基础1.局域网和广域网2.协议初识和网络协议分层&#xff08;TCP/IP四层模型&#xff09;3.MAC地址和IP地址&#xff08;子网掩码&#xff0c;路由表&#xff0c;I…

美国金融科技公司SoFi的增长难以持久,股价也将下跌

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 公司介绍 SoFi Technologies(SoFi)是一家来自美国的知名金融科技公司&#xff0c;自2011年成立以来&#xff0c;已成为领先的个人理财在线平台。SoFi为年轻的高收入客户提供多样化的产品和服务&#xff0c;包括学生和汽车贷…

如何在 Python 中使用断点调试

入门教程、案例源码、学习资料、读者群 请访问&#xff1a; python666.cn 实际上没人能一次就写出完美的代码&#xff0c;除了我。但是世界上只有一个我。 林纳斯托瓦兹&#xff08;Linux 之父&#xff09; 大家好&#xff0c;欢迎来到 Crossin的编程教室 &#xff01; 上面这段…

【CSS3系列】第二章 · CSS3 新增盒模型和背景属性

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

大数据:数据表操作,分区表,分桶表,修改表,array,map, struct

大数据&#xff1a;数据表操作&#xff0c;分区表 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&a…

【能量算子】评估 EEG 中的瞬时能量:非负、频率加权能量算子(PythonMatlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

五种方法提升Midjourney的出图品质

本文基于B站UP主琥珀川Eric的《五种方法提升Midjourney出图品质》制作在此感谢大神的分享。 本文全面介绍以上五种提升Midjourney出图品质的方法&#xff0c;简单实用&#xff0c;马上就可以用上。Lets go&#xff01;&#xff01;&#xff01; 方法一 使用相机参数创建逼真的图…

windows系统编译的Qt程序转到国产化麒麟linux中编译

团队自研股票软件&#xff0c;关威信共总号&#xff1a;QStockView&#xff0c;下载 1.1 windows系统编译的Qt程序转到国产化麒麟linux中编译 &#xff08;1&#xff09;把Vs工程项目文件导入到Linux中 首先把vs的工程拷贝到linux里面&#xff08;可以用虚拟机的共享文件夹…

适配器模式的学习与使用

1、适配器模式的学习 当我们需要将一个类的接口转换成另一个客户端所期望的接口时&#xff0c;适配器模式&#xff08;Adapter Pattern&#xff09;可以派上用场。它允许不兼容的接口之间能够协同工作。   适配器模式属于结构型设计模式&#xff0c;它包含以下几个角色&#…

2、数据库:SQL Server部署 - 系统部署系列文章

对于微软的SQL Server的安装&#xff0c;以前已经有写过了&#xff0c;到了2022版本&#xff0c;安装没多大的改变&#xff0c;很多只需要少配置&#xff0c;然后直接下一步即可。现在是2023年了&#xff0c;SQL Server已经出到了2022版本&#xff0c;这篇博文就再次对SQL Serv…