1. 程式人生 > 其它 >java規則引擎easy-rules使用指南 1 - 基本用法

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 釋出!