EasyRule实战

7,259 阅读4分钟

1.简述

Easy Rules是一个简单而强大的Java规则引擎,提供以下功能:

  • 轻量级框架和易于学习的API
  • 基于POJO的开发与注解的编程模型
  • 定义抽象的业务规则并轻松应用它们
  • 支持从简单规则创建组合规则的能力
  • 支持使用表达式语言(如MVEL和SpEL)定义规则的能力

2.EasyRule的支持环境

EasyRule支持jdk1.7以上的版本。 maven依赖

<!--easy rules核心库-->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-core</artifactId>
    <version>4.1.0</version>
</dependency>

<!--规则定义文件格式,支持json,yaml等-->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-support</artifactId>
    <version>4.1.0</version>
</dependency>

<!--支持mvel规则语法库-->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-mvel</artifactId>
    <version>4.1.0</version>
</dependency>

3.定义规则引擎

ParameterTypeRequiredDefaultcontent
skipOnFirstAppliedRulebooleannoMaxInt告诉引擎在规则失败时跳过后面的规则
skipOnFirstFailedRulebooleannoMaxInt告诉引擎一个规则不会被触发跳过后面的规则
skipOnFirstNonTriggeredRulebooleannoMaxInt告诉引擎一个规则不会被触发跳过后面的规则
rulePriorityThresholdbooleannoMaxInt告诉引擎如果优先级超过定义的阈值,则跳过下一个规则。版本3.3已经不支持更改,默认MaxInt

3.1 rulePriorityThreshold

在创建规则引擎时,我们将其属性 rulePriorityThreshold 的值设置为了 1,这样的设置后的效果相当于在定义的所有规则中将 priority > 1 的规则去掉,换种说法就是只考虑 priority的值小于等于 1 的规则。

3.2 skipOnFirstAppliedRule

所有规则按照优先级从高到低的顺序进行判断,当发现一个满足条件的规则并执行了相关操作后,便不再继续判断其他规则

3.3 skipOnFirstNonTriggeredRule

所有规则按照优先级从高到低的顺序进行判断,如果满足当前的规则,则执行相应的操作,直到遇到不满足条件的规则为止,并且也不会对其他规则进行判断了

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

4.定义规则

4.1 使用rule定义

我们将创建一个始终被触发的规则,在执行时将“hello world”打印到控制台。规则如下:

public class RuleClass {

    @Rule(priority = 1)
    public static class FizzRule {
        @Condition
        public boolean isFizz(@Fact("number") Integer number) {
            return number % 5 == 0;
        }

        @Action
        public void printFizz() {
            System.out.print("fizz");
        }
    }

    @Rule(priority = 2)
    public static class BuzzRule {
        @Condition
        public boolean isBuzz(@Fact("number") Integer number) {
            return number % 7 == 0;
        }

        @Action
        public void printBuzz() {
            System.out.print("buzz");
        }
    }

    public static class FizzBuzzRule extends UnitRuleGroup {

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

        @Override
        public int getPriority() {
            return 0;
        }
    }

    @Rule(priority = 3)
    public static class NonFizzBuzzRule {

        @Condition
        public boolean isNotFizzNorBuzz(@Fact("number") Integer number) {
            // can return true, because this is the latest rule to trigger according to
            // assigned priorities
            // and in which case, the number is not fizz nor buzz
            return number % 5 != 0 || number % 7 != 0;
        }

        @Action
        public void printInput(@Fact("number") Integer number) {
            System.out.print(number);
        }
    }

}

@Condition注解标记计算规则条件的方法。此方法必须是公共的,可以有一个或多个用@Fact注解的参数,并返回布尔类型。只有一个方法能用@Condition注解。

@Action注解标记要执行规则操作的方法。规则可以有多个操作。可以使用order属性按指定的顺序执行操作。默认情况下,操作的顺序为0。


public class RuleClient {
    public static void main(String[] args) {
        // create a rules engine
        RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
        RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);

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

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

}

4.2 使用RuleBuilder API定义规则

Rule rule = new RuleBuilder()
    .name("myRule")
    .description("myRuleDescription")
    .priority(3)
    .when(condition)
    .then(action1)
    .then(action2)
    .build();

4.3 yml文件定义

规则文件的文件名为 rules.yml ,其内容如下

---
name: "three"
description: "print three"
priority: 0
condition: "number % 3 == 0"
actions:
 - "System.out.println("three")"
 
---
name: "five"
description: "print five"
priority: 1
condition: "number % 5 == 0"
actions:
 - "System.out.println("five")"
 
---
name: "seven"
description: "print seven"
priority: 2
condition: "number % 7 == 0"
actions:
 - "System.out.println("seven")"
 
---
name: "itself"
description: "print the number itself otherwise"
priority: 3
condition: "true"
actions:
 - "System.out.println(number)"

客户端调用


public class RuleClient {

    public static void main(String[] args) throws FileNotFoundException {
        RulesEngine rulesEngine = new DefaultRulesEngine();
        Rules rules = MVELRuleFactory.createRulesFrom(new FileReader("rules.yml"));

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

4.4 MVELRule


public class MVELTestRule {

    public static void main(String[] args) {

        //规则引擎
//        RulesEngine rulesEngine = new DefaultRulesEngine();
        DefaultRulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.registerRuleListener(new MyRuleListener());

//        ((DefaultRulesEngine) rulesEngine).registerRuleListener(new MyRuleListener());

        //规则
        MVELRule ageRule = new MVELRule()
                .name("my rule")
                .description("test demo rule")
                .priority(1)
                .when("user.age > 18")
                .then("map.put('code',200);map.put('msg','success');myResult.setCode('200');myResult.setMsg('success');");

        Rules rules = new Rules();
        rules.register(ageRule);

        Facts facts = new Facts();

        User user   = new User();
        user.setAge(19);
        facts.put("user", user);
        Map<String, String> map  = new HashMap();
        MyResult myResult = new MyResult();
        facts.put("map", map);
        facts.put("myResult", myResult);

        rulesEngine.fire(rules, facts);
        System.out.println(map);
        System.out.println(myResult);

    }
}

5.RuleListener

public class MyRuleListener implements RuleListener {

    @Override
    public boolean beforeEvaluate(Rule rule, Facts facts) {
      return true;  
    }

    @Override
    public void afterEvaluate(Rule rule, Facts facts, boolean b) {
       System.out.println("---MyRuleListener------afterEvaluate-----");
    }

    @Override
    public void beforeExecute(Rule rule, Facts facts) {
      System.out.println("---MyRuleListener------beforeExecute-----");
    }

    @Override
    public void onSuccess(Rule rule, Facts facts) {
 
      System.out.println("---MyRuleListener------onSuccess-----");
    }

    @Override
    public void onFailure(Rule rule, Facts facts, Exception e) {
       System.out.println("---MyRuleListener------onFailure-----");
    }
 
 }
  • beforeEvaluate 该方法在执行@Condition修饰的方法之前执行。该方法返回false则不执行条件的判断,直接跳过该当前rule。
  • afterEvaluate 该方法在执行@Condition修饰的方法之后执行。
  • beforeExecute 该方法在执行@Action修饰的方法之前执行。
  • onSuccess 该方法在执行@Action修饰的方法之后执行。
  • onFailure 在执行@Action修饰的方法出现异常时,该方法执行。

rulesEngine.fire(rules, facts);之前注册规则监听器

  ((DefaultRulesEngine) rulesEngine).registerRuleListener(new MyRuleListener());

6.RulesEngineListener

public class MyRulesEngineListener implements RulesEngineListener {
    @Override
    public void beforeEvaluate(Rules rules, Facts facts) {
        System.out.println("---MyRulesEngineListener------beforeEvaluate-----");
    }

    @Override
    public void afterExecute(Rules rules, Facts facts) {
        System.out.println("---MyRulesEngineListener------afterExecute-----");
    }
}

  • beforeEvaluate 该方法在执行@Action修饰的方法之后执行。在RuleListener之前执行
  • afterExecute 该方法在执行@Condition修饰的方法之前执行。在RuleListener之后执行

rulesEngine.fire(rules, facts);之前注册规则引擎监听器

((DefaultRulesEngine) rulesEngine).registerRulesEngineListener(new MyRulesEngineListener());

7.运行规则

// create facts
Facts facts = new Facts();

// create rules
Rules rules = new Rules();
rules.register(new HelloWorldRule());

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