规则引擎 - EasyRules

4,006 阅读5分钟

EasyRule概述

Easy-Rules是一款轻量级的规则引擎.

框架特点

  • 轻量级类库和容易上手

  • 基于POJO的开发与注解的编程模型

  • 方便且适用于java的抽象的业务模型规则

  • 支持从简单的规则创建组合规则

官方地址: github.com/j-easy/easy…

应用示例

入门示例

定义一个规则,在任何情况下都输出Hello world.

public class HelloRoles {

    @Rule(name = "Hello World Rule", description = "Always say hello world")
    public static class HelloWorldRule {

        @Condition
        public boolean when() {
            return true;
        }

        @Action
        public void then() throws Exception {
            System.out.println("hello world");
        }

    }

    public static void main(String[] args) {

        // 创建规则
        Rules rules = new Rules();
        rules.register(new HelloWorldRule());

        // 定义行为
        Facts facts = new Facts();

        // 将规则应用到行为上
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);

    }
}

执行输出

[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Engine parameters { skipOnFirstAppliedRule = false, skipOnFirstNonTriggeredRule = false, skipOnFirstFailedRule = false, priorityThreshold = 2147483647 }
hello world
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Registered rules:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'Hello World Rule', description = 'Always say hello world', priority = '2147483646'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Known facts:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rules evaluation started
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'Hello World Rule' triggered
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'Hello World Rule' performed successfully

通过API可以看到我们可以注册多个规则, 多个规则之间处理顺序可以通过设定优先级来定义, 规则优先级的上限值默认是Integer.MAX_VALUE(2147483647). 相同的优先级执行顺序按照规则名称的自然顺序执行.

优先级示例

不同优先级

代码示例

public class WeatherRules {

    @Rule(name = "weather rule1", description = "simple rule1", priority = 2)
    public static class WeatherRule1 {

        @Condition
        public boolean when(@Fact("rain") boolean rain) {
            return rain;
        }

        @Action
        public void then() {
            System.out.println("It rains, with rule1 .");
        }
    }

    @Rule(name = "weather rule2", description = "simple rule2", priority = 1)
    public static class WeatherRule2 {

        @Condition
        public boolean when(@Fact("rain") boolean rain) {
            return rain;
        }

        @Action
        public void then() {
            System.out.println("It rains, with rule2 .");
        }
    }

    public static void main(String[] args) throws Exception{
        Rules rules = new Rules();

        rules.register(new WeatherRule1());
        rules.register(new WeatherRule2());

        Facts facts = new Facts();
        facts.put("rain", true);

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

}

执行输出

[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Engine parameters { skipOnFirstAppliedRule = false, skipOnFirstNonTriggeredRule = false, skipOnFirstFailedRule = false, priorityThreshold = 2147483647 }
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Registered rules:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'weather rule2', description = 'simple rule2', priority = '1'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'weather rule1', description = 'simple rule1', priority = '2'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Known facts:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Fact { rain : true }
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rules evaluation started
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule2' triggered
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule2' performed successfully
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule1' triggered
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule1' performed successfully
It rains, with rule2 .
It rains, with rule1 .

通过注册规则可以看到, 注册时是按照优先级注册的, 并不是按照我们代码的顺序注册的. 执行校验输出时也是按照优先级校验然后匹配给出结果. 根据@Condition条件命中来进行对应的规则输出(相同条件可以定制多个规则, 规则命中则执行)

相同优先级

代码示例

public class WeatherRules {

    @Rule(name = "weather rule1", description = "simple rule with b", priority = 1)
    public static class WeatherRule1 {

        @Condition
        public boolean when(@Fact("rain") boolean rain) {
            return rain;
        }

        @Action
        public void then() {
            System.out.println("It rains, with rule1 .");
        }
    }

    @Rule(name = "weather rule2", description = "simple rule with a", priority = 1)
    public static class WeatherRule2 {


        @Condition
        public boolean when(@Fact("rain") boolean rain) {
            return rain;
        }

        @Action
        public void then() {
            System.out.println("It rains, with rule2 .");
        }
    }

    public static void main(String[] args) throws Exception{
        Rules rules = new Rules();

        rules.register(new WeatherRule2());
        rules.register(new WeatherRule1());

        Facts facts = new Facts();
        facts.put("rain", true);

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

}

执行输出

[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Engine parameters { skipOnFirstAppliedRule = false, skipOnFirstNonTriggeredRule = false, skipOnFirstFailedRule = false, priorityThreshold = 2147483647 }
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Registered rules:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'weather rule1', description = 'simple rule with b', priority = '1'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'weather rule2', description = 'simple rule with a', priority = '1'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Known facts:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Fact { rain : true }
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rules evaluation started
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule1' triggered
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule1' performed successfully
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule2' triggered
It rains, with rule1 .
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule2' performed successfully
It rains, with rule2 .

可以看到, 注册顺序在优先级相同的时候是按照规则名称的自然顺序注册, 校验的时候按照注册顺序校验. 这种场景我们也可以通过下面方式实现,同一事件条件绑定多个依赖逻辑.

public class WeatherRules {


    @Rule(name = "weather rule Composite", description = "simple rule with order action", priority = 1)
    public static class WeatherRuleComposite{

        @Condition
        public boolean when(@Fact("rain") boolean rain) {
            return rain;
        }

        @Action(order = 1)
        public void then1() {
            System.out.println("It rains, with rule1 .");
        }

        @Action(order = 2)
        public void then2() {
            System.out.println("It rains, with rule2 .");
        }

    }

    public static void main(String[] args) throws Exception{
        Rules rules = new Rules();

        rules.register(new WeatherRuleComposite());

        Facts facts = new Facts();
        facts.put("rain", true);

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

}

执行输出

[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Engine parameters { skipOnFirstAppliedRule = false, skipOnFirstNonTriggeredRule = false, skipOnFirstFailedRule = false, priorityThreshold = 2147483647 }
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Registered rules:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'weather rule Composite', description = 'simple rule with order action', priority = '1'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Known facts:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Fact { rain : true }
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rules evaluation started
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule Composite' triggered
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule Composite' performed successfully
It rains, with rule1 .
It rains, with rule2 .

硬编码示例

上面的示例代码也可以通过硬编码方式实现

public class ApiRules {

    public static Rule ruleComposite(){
        Rule rule = new RuleBuilder()
                .name("weather rule Composite")
                .description("simple rule with order action")
                .priority(1)
                .when(new Condition() {
                    @Override
                    public boolean evaluate(Facts facts) {
                        if((Boolean) facts.get("rain") == true){
                            return true;
                        }
                        return false;
                    }
                })
                .then(new Action() {
                    @Override
                    public void execute(Facts facts) throws Exception {
                        if((Boolean) facts.get("rain") == true){
                            System.out.println("It rains, with rule1 .");
                        }
                    }
                })
                .then(new Action() {
                    @Override
                    public void execute(Facts facts) throws Exception {
                        if((Boolean) facts.get("rain") == true){
                            System.out.println("It rains, with rule2 .");
                        }
                    }
                })
                .build();
        return rule;
    }

    public static void main(String[] args) throws Exception{
        Rules rules = new Rules();
        rules.register(ruleComposite());

        Facts facts = new Facts();
        facts.put("rain", true);

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

多规则示例

代码示例

public class WeatherRules {

    @Rule(name = "weather rule1", description = "simple rule1", priority = 1)
    public static class WeatherRule1 {

        @Condition
        public boolean when(@Fact("rain") boolean rain) {
            return rain;
        }

        @Action
        public void then() {
            System.out.println("It rains, with rule1 .");
        }
    }

    @Rule(name = "weather rule2", description = "simple rule2", priority = 3)
    public static class WeatherRule2 {

        @Condition
        public boolean when(@Fact("rain") boolean rain) {
            return rain;
        }

        @Action
        public void then() {
            System.out.println("It rains, with rule2 .");
        }
    }


    @Rule(name = "weather rule3", description = "simple rule3", priority = 2)
    public static class WeatherRule3 {

        @Condition
        public boolean when(@Fact("sun") boolean sun) {
            return sun;
        }

        @Action
        public void then() {
            System.out.println("It sun, with rule3 .");
        }

    }

    public static void main(String[] args) throws Exception{
        Rules rules = new Rules();


        rules.register(new WeatherRule1());
        rules.register(new WeatherRule2());
        rules.register(new WeatherRule3());

        Facts facts = new Facts();
        facts.put("rain", true);
        facts.put("sun", true);

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

}

执行输出

It rains, with rule1 .
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Engine parameters { skipOnFirstAppliedRule = false, skipOnFirstNonTriggeredRule = false, skipOnFirstFailedRule = false, priorityThreshold = 2147483647 }
It sun, with rule3 .
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Registered rules:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'weather rule1', description = 'simple rule1', priority = '1'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'weather rule3', description = 'simple rule3', priority = '2'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rule { name = 'weather rule2', description = 'simple rule2', priority = '3'}
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Known facts:
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Fact { rain : true }
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Fact { sun : true }
[main] INFO org.jeasy.rules.core.DefaultRulesEngineListener - Rules evaluation started
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule1' triggered
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule1' performed successfully
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule3' triggered
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule3' performed successfully
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule2' triggered
[main] INFO org.jeasy.rules.core.DefaultRuleListener - Rule 'weather rule2' performed successfully
It rains, with rule2 .

执行按照规则注册的优先级, 规则1>规则3>规则2, 进行场景条件判断, rain=true时输出rains相关打印、sun=true时输出 sun相关打印.

参考文档