java规则引擎easy-rules使用指南 1 - 基本用法

规则引擎能干什么

规则引擎的工作方式有点像if-else,它允许你设置一些条件和动作,然后在程序运行时判断某些动作该不该执行。
easy-rules是一款轻量级的java规则引擎,目前它的长期支持版本是4.1.x,所以我们就以4.1.0版本来看一下如何使用。

如何引入

如果使用maven,可以直接在pom中加入:

<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-core</artifactId>
    <version>4.1.0</version>
</dependency>

如果需要对MVEL, SpEL和JEXL表达式的支持,还需要引入相应的支持包:

<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>

<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-jexl</artifactId>
    <version>4.1.0</version>
</dependency>

一个简单的例子

使用easy-rules非常简单,只需要两个步骤:

  • 创建规则和动作
  • 运行引擎

以下是一个简单的例子:

public class Test {
    public static void main(String[] args) {
            // define rules 
                Rule weatherRule = new RuleBuilder()
        .name("weather rule")
        .description("if it rains then take an umbrella")
        .when(facts -> facts.get("rain").equals(true))
        .then(facts -> System.out.println("It rains, take an umbrella!"))
        .build();
        Rules rules = new Rules();
        rules.register(weatherRule);
        
        // define facts
        Facts facts = new Facts();
        facts.put("rain", true);

        // fire rules on known facts
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);
    }
}

例子中的weatherRule是构建的条件和动作,其中“when”里面的是条件,“then”里面的是满足条件后需要执行的动作。
facts是运行中实际的数据。
这个例子会在控制台打印出:It rains, take an umbrella!

如何使用Rule

1 介绍

Rule用于定义规则和行为,它可以设置以下这些属性:

  • name: 命名空间中的唯一规则名称
  • description: 描述
  • priority: 优先级
  • when:规则
  • then:行为

当when中的表达式返回true时,将执行then中的表达式。
then里面除了可以执行方法外,也可以修改Facts里面的数据。

2 Rule的多种写法

easy-rules提供了多种定义Rule的写法,还是以上面的例子举例,下面的Rule写法与上面例子中的写法是等价的。

2.1 使用RuleBuilder

Rule weatherRule = new RuleBuilder()
        .name("weather rule")
        .description("if it rains then take an umbrella")
        .when(facts -> facts.get("rain").equals(true))
        .then(facts -> System.out.println("It rains, take an umbrella!"))
        .build();

2.2 使用注解

@Rule(name = "weather rule", description = "if it rains then take an umbrella")
public class WeatherRule {

    @Condition
    public boolean itRains(@Fact("rain") boolean rain) {
        return rain;
    }
    
    @Action
    public void takeAnUmbrella() {
        System.out.println("It rains, take an umbrella!");
    }
}

2.3 使用表达式语言

Rule weatherRule = new MVELRule()
        .name("weather rule")
        .description("if it rains then take an umbrella")
        .when("rain == true")
        .then("System.out.println(\"It rains, take an umbrella!\");");

使用表达式语言需要引入对应的支持包,比如这里使用了MVEL,那么需要在pom中引入easy-rules-mvel这个包

2.4 使用yml配置文件

name: "weather rule"
description: "if it rains then take an umbrella"
condition: "rain == true"
actions:
  - "System.out.println(\"It rains, take an umbrella!\");"
MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));

3 复合规则

有时候我们需要把多个规则放在一起使用,就好像写多层if-else一样。
easy-rules为此提供了三个对象来支持复合规则的使用:

  • UnitRuleGroup:要么应用所有rule,要么不应用任何rule。
  • ActivationRuleGroup:它触发第一个适用的rule并忽略组中的其他rule(XOR 逻辑)。rule首先按其在组内的自然顺序排序,如果设置了priority,那么数字越小的优先级越高。
  • ConditionalRuleGroup::只有具有最高优先级的rule评估为真,才触发其余rule。

下面是使用示范:

//Create a composite rule from two primitive rules
UnitRuleGroup myUnitRuleGroup =
    new UnitRuleGroup("myUnitRuleGroup", "unit of myRule1 and myRule2");
myUnitRuleGroup.addRule(myRule1);
myUnitRuleGroup.addRule(myRule2);

//Register the composite rule as a regular rule
Rules rules = new Rules();
rules.register(myUnitRuleGroup);

RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, someFacts);

如何使用facts

Fact是用来装需要判断的数据的,它的API定义如下:

public class Fact<T> {
   private final String name;
   private final T value;
}

使用的时候直接用Facts,就跟Map用法差不多:

Facts facts = new Facts();
facts.put("foo", "bar");

Rule的“then”代码中可以修改facts的数据,可以使用这个特性来获取返回值。
例如:

Rule ageRule = new MVELRule()
        .name("age rule")
        .description("Check if person's age is > 18 and marks the person as adult")
        .priority(1)
        .when("person.age > 18")
        .then("person.setAdult(true);");

注意:

  • 如果when方法中缺少注入的Fact,引擎将记录一个警告并认为条件评估为false。
  • 如果then方法中缺少注入的Rule,则不会执行该操作,并且引擎将抛出一个org.jeasy.rules.core.NoSuchFactException.

如何使用Engine

1 Engine的两种实现

Easy Rules 提供了两种RulesEngine接口实现:

DefaultRulesEngine:根据其自然顺序应用规则(默认为优先级)。
InferenceRulesEngine:不断地对已知事实应用规则,直到不再适用规则为止。

DefaultRulesEngine的作用很好理解,就像上面那些例子表现出来的一样。
InferenceRulesEngine则相当于在DefaultRulesEngine的基础上加了一个循环。还是以开头的代码举例,换成InferenceRulesEngine。
这时控制台将重复打印“It rains, take an umbrella!”。

public class Test {
    public static void main(String[] args) {
            // define rules 
                Rule weatherRule = new RuleBuilder()
        .name("weather rule")
        .description("if it rains then take an umbrella")
        .when(facts -> facts.get("rain").equals(true))
        .then(facts -> System.out.println("It rains, take an umbrella!"))
        .build();
        Rules rules = new Rules();
        rules.register(weatherRule);
        
        // define facts
        Facts facts = new Facts();
        facts.put("rain", true);

        // fire rules on known facts
        RulesEngine rulesEngine = new InferenceRulesEngine();
        rulesEngine.fire(rules, facts);
    }
}

2 Engine的配置参数

Engine支持以下几个参数配置:

范围 类型 必需的 默认
rulePriorityThreshold int no MaxInt
skipOnFirstAppliedRule boolean no false
skipOnFirstFailedRule boolean no false
skipOnFirstNonTriggeredRule boolean no false
  • skipOnFirstAppliedRule:在应用规则时跳过下一个规则。
  • skipOnFirstFailedRule:在规则失败时跳过下一个规则。
  • skipOnFirstNonTriggeredRule:在未触发规则时跳过下一个规则。
  • rulePriorityThreshold:priority 大于rulePriorityThreshold时跳过下一个规则。

写法如下:

RulesEngineParameters parameters = new RulesEngineParameters()
    .rulePriorityThreshold(10)
    .skipOnFirstAppliedRule(true)
    .skipOnFirstFailedRule(true)
    .skipOnFirstNonTriggeredRule(true);

RulesEngine rulesEngine = new DefaultRulesEngine(parameters);

本文由博客一文多发平台 OpenWrite 发布!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容