什么是规则引擎
规则引擎就是提供一种可选的计算模型,与通常的命令式模型(由带有条件和循环的命令依次组成)不同,规则引擎基于生产规则系统。这是一组生产规则,每条规则都有一个条件(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 判断是否满足规则条件
接口:Action 执行具体规则
入门代码案例
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();
}
}
}