什么是规则引擎

规则引擎就是提供一种可选的计算模型,与通常的命令式模型(由带有条件和循环的命令依次组成)不同,规则引擎基于生产规则系统。这是一组生产规则,每条规则都有一个条件(condition)和一个动作(action),简单来说,可以看作一组if-then语句。
精妙之处在于规则可以按任何顺序编码,引擎会决定何时使用对顺序有意义的任何方式来计算它们,就达到了自由组合,解耦合的特型。
 

easy-rules规则引擎

源码地址:https://github.com/taotao419/easyrules

easy rules的特点

  • 轻量级库和易于学习的API
  • 基于POJO的注解编程模型开发
  • 基于mvel表达式的编程模型
  • 支持根据简单的规则创建组合规则
  • 方便且适用于java的抽象的业务模型规则

easy-rules案例说明

基于maven引入

<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-core</artifactId>
    <version>4.1.1</version>
</dependency>
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-support</artifactId>
    <version>4.1.1</version>
</dependency>
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-mvel</artifactId>
    <version>4.1.1</version>
</dependency>

定义规则

大多数业务规则可以用以下定义表示:

  • Name : 一个命名空间下的唯一的规则名称
  • Description : 规则的简要描述
  • Priority : 相对于其他规则的优先级
  • Facts : 事实,可立即为要处理的数据
  • Conditions : 为了应用规则而必须满足的一组条件
  • Actions : 当条件满足时执行的一组动作 

核心类说明

接口:Condition  判断是否满足规则条件

Drool5规则引擎 statelessKieSession如何指定匹配某个规则 规则引擎easy rule_优先级

接口:Action 执行具体规则

Drool5规则引擎 statelessKieSession如何指定匹配某个规则 规则引擎easy rule_编程语言_02

入门代码案例

java代码实现

package org.jeasy.rules.tutorials.airco;

import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.core.InferenceRulesEngine;
import org.jeasy.rules.core.RuleBuilder;

/**
 * 例子:温度大于25度就需要开空调降温,小于25度就关空调
 */
public class Launcher {

    public static void main(String[] args) {
        Facts facts = new Facts();
        //设置温度
        facts.put("temperature", 30);

        //自定义规则,开空调规则
        Rule airConditioningRule = new RuleBuilder()
                .name("开空调规则")
                .when(HighTemperatureCondition.itIsHot())
                .then(DecreaseTemperatureAction.decreaseTemperatureAction())
                .build();

        //创建规则组
        Rules rules = new Rules();
        //注册规则
        rules.register(airConditioningRule);

        //创建规则引擎,根据规则的自然顺序(默认为优先级)执行规则,只执行一次
        RulesEngine rulesEngine = new DefaultRulesEngine();
        //创建规则引擎,持续执行规则,直到没有满足规则为止
//        RulesEngine rulesEngine = new InferenceRulesEngine();
        //开始执行
        rulesEngine.fire(rules, facts);
    }
}
package org.jeasy.rules.tutorials.airco;

import org.jeasy.rules.api.Action;
import org.jeasy.rules.api.Facts;

/**
 * 开空调后温度降1,
 */
public class DecreaseTemperatureAction implements Action {

    static DecreaseTemperatureAction decreaseTemperatureAction() {
        return new DecreaseTemperatureAction();
    }

    @Override
    public void execute(Facts facts) throws Exception {
        //得到温度
        Integer temperature = facts.get("temperature");
        System.out.println("空调运行中,正在持续降温, 当前温度temperature=" + temperature);
        //温度降1
        facts.put("temperature", temperature - 1);
    }
}
package org.jeasy.rules.tutorials.airco;

import org.jeasy.rules.api.Condition;
import org.jeasy.rules.api.Facts;
/**
 * 判断温度是否大于25度
 */
public class HighTemperatureCondition implements Condition {

    static HighTemperatureCondition itIsHot() {
        return new HighTemperatureCondition();
    }

    @Override
    public boolean evaluate(Facts facts) {
        //拿到温度值
        Integer temperature = facts.get("temperature");
        //判断是否大于25度
        return temperature > 25;
    }
}

注解实现:

package org.jeasy.rules.tutorials.airco;

import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Rule;
import org.jeasy.rules.api.Facts;

/**
 * @program: easy-rules
 * @description: 是否开空调规则
 * @create: 2020-12-30 14:42
 **/
@Rule(name = "Hello World rule", description = "Always say hello world")
public class TemperatureRule {

    @Condition
    public boolean evaluate(Facts facts) {
        //拿到温度值
        Integer temperature = facts.get("temperature");
        //判断是否大于25度
        return temperature > 25;
    }

    @Action
    public void execute(Facts facts) throws Exception {
        //得到温度
        Integer temperature = facts.get("temperature");
        System.out.println("空调运行中,正在持续降温, 当前温度temperature=" + temperature);
        //温度降1
        facts.put("temperature", temperature - 1);
    }
}
package org.jeasy.rules.tutorials.airco;

import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
/**
 * 例子:温度大于25度就需要开空调降温,小于25度就关空调
 */
public class Launcher {

    public static void main(String[] args) {
        Facts facts = new Facts();
        //设置温度
        facts.put("temperature", 30);
        //创建规则组
        Rules rules = new Rules();
        //注册规则
        rules.register(new TemperatureRule());

        //创建规则引擎,根据规则的自然顺序(默认为优先级)执行规则,只执行一次
        RulesEngine rulesEngine = new DefaultRulesEngine();
        //创建规则引擎,持续执行规则,直到没有满足规则为止
//        RulesEngine rulesEngine = new InferenceRulesEngine();
        //开始执行
        rulesEngine.fire(rules, facts);
    }
}

yml实现

创建temperature-rule.yml文件内容如下:

name: "Hello World rule"
description: "Always say hello world"
priority: 1
condition: "temperature > 25"
actions:
  - "System.out.println(\"空调运行中,正在持续降温, 当前温度temperature=\" + temperature);"
package org.jeasy.rules.tutorials.airco;

import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.mvel.MVELRuleFactory;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.support.reader.YamlRuleDefinitionReader;
import java.io.FileReader;

/**
 * 例子:温度大于25度就需要开空调降温,小于25度就关空调
 */
public class Launcher {

    public static void main(String[] args) throws Exception {
        Facts facts = new Facts();
        //设置温度
        facts.put("temperature", 30);
        //加载yml规则配置文件
        MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
        String fileName = args.length != 0 ? args[0] : "easy-rules-tutorials/src/main/java/org/jeasy/rules/tutorials/airco/temperature-rule.yml";
        Rule temperatureRule = ruleFactory.createRule(new FileReader(fileName));

        //创建规则组
        Rules rules = new Rules();
        //注册规则
        rules.register(temperatureRule);

        //创建规则引擎,根据规则的自然顺序(默认为优先级)执行规则,只执行一次
        RulesEngine rulesEngine = new DefaultRulesEngine();
        //开始执行
        rulesEngine.fire(rules, facts);
    }
}

深入使用说明

规则引擎参数说明

  • skipOnFirstAppliedRule:告诉引擎规则被触发时跳过后面的规则。
  • skipOnFirstFailedRule:告诉引擎在规则失败时跳过后面的规则。
  • skipOnFirstNonTriggeredRule:告诉引擎一个规则不会被触发跳过后面的规则。
  • rulePriorityThreshold:告诉引擎如果优先级超过定义的阈值,则跳过下一个规则。版本3.3已经不支持更改,默认MaxInt。
RulesEngineParameters parameters = new RulesEngineParameters()
    .rulePriorityThreshold(10)
    .skipOnFirstAppliedRule(true)
    .skipOnFirstFailedRule(true)
    .skipOnFirstNonTriggeredRule(true);

RulesEngine rulesEngine = new DefaultRulesEngine(parameters);

规则组类说明

  • UnitRuleGroup: 要么应用所有规则,要么不应用任何规则,整个组中得规则都要成功。
  • ActivationRuleGroup: 它触发第一个适用规则,并忽略组中的其他规则。
  • ConditionalRuleGroup: 如果具有最高优先级的规则计算结果为true,只执行优先级最高得规则。

执行多规则代码案例

package org.jeasy.rules.tutorials.fizzbuzz;

import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Priority;
import org.jeasy.rules.annotation.Rule;
/**
 * 打印出能被7整除的整数
 */
@Rule
public class BuzzRule {

    @Condition
    public boolean isBuzz(@Fact("number") Integer number) {
        return number % 7 == 0;
    }

    @Action
    public void printBuzz(@Fact("number") Integer number) {
        System.out.println("打印出能被7整除的整数------->number=" + number);
    }

    @Priority
    public int getPriority() {
        return 2;
    }
}
package org.jeasy.rules.tutorials.fizzbuzz;

import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Priority;
import org.jeasy.rules.annotation.Rule;
/**
 * 打印出能被5整除的整数
 */
@Rule
public class FizzRule {

    @Condition
    public boolean isFizz(@Fact("number") Integer number) {
        return number % 5 == 0;
    }

    @Action
    public void printFizz(@Fact("number") Integer number) {
        System.out.println("打印出能被5整除的整数------->number="+number);
    }

    @Priority
    public int getPriority() {
        return 1;
    }
}
package org.jeasy.rules.tutorials.fizzbuzz;

import org.jeasy.rules.support.composite.ActivationRuleGroup;
import org.jeasy.rules.support.composite.ConditionalRuleGroup;
import org.jeasy.rules.support.composite.UnitRuleGroup;
/**
 * 规则组
 * UnitRuleGroup: 要么应用所有规则,要么不应用任何规则,整个组中得规则都要成功
 * ActivationRuleGroup: 它触发第一个适用规则,并忽略组中的其他规则
 * ConditionalRuleGroup: 如果具有最高优先级的规则计算结果为true,只执行优先级最高得规则
 */
//public class FizzBuzzRule extends UnitRuleGroup {
//public class FizzBuzzRule extends ActivationRuleGroup {
public class FizzBuzzRule extends ConditionalRuleGroup {

    public FizzBuzzRule(Object... rules) {
        for (Object rule : rules) {
            addRule(rule);
        }
    }

    @Override
    public int getPriority() {
        return 0;
    }
}
package org.jeasy.rules.tutorials.fizzbuzz;

import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.api.RulesEngineParameters;

public class FizzBuzzWithEasyRules {
    public static void main(String[] args) {
        //create rules engine
        //skipOnFirstAppliedRule 为true时, 从第一条开始,匹配一条就会跳过后面规则匹配,不匹配则一直往下执行
        //skipOnFirstFailedRule 为true时, 如果执行@Action中发生异常就会跳过后面规则匹配
        //skipOnFirstNonTriggeredRule 为true时,从第一条开始,匹配一条才会往下执行,不匹配则跳过后面
        //rulePriorityThreshold 大于指定的优先级则不进行匹配
        RulesEngineParameters parameters = new RulesEngineParameters()
                                            .skipOnFirstAppliedRule(true);
//                                            .skipOnFirstFailedRule(true);
//                                            .skipOnFirstNonTriggeredRule(true);

        RulesEngine rulesEngine = new DefaultRulesEngine(parameters);

        //create rules
        Rules rules = new Rules();
        rules.register(new FizzRule());
        rules.register(new BuzzRule());
//        rules.register(new FizzBuzzRule(new FizzRule(), new BuzzRule()));

        Facts facts = new Facts();
        for (int i = 1; i <= 100; i++) {
            facts.put("number", i);
            rulesEngine.fire(rules, facts);
            System.out.println();
        }
    }
}