轻量级规则引擎EasyRules

EasyRule概述

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

框架特点

  • 轻量级类库和容易上手

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

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

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

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

应用示例

入门示例

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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);

}
}

执行输出

1
2
3
4
5
6
7
8
[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). 相同的优先级执行顺序按照规则名称的自然顺序执行.

优先级示例

不同优先级

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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);
}

}

执行输出

1
2
3
4
5
6
7
8
9
10
11
12
13
[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条件命中来进行对应的规则输出(相同条件可以定制多个规则, 规则命中则执行)

相同优先级

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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);
}

}

执行输出

1
2
3
4
5
6
7
8
9
10
11
12
13
[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 .

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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);
}

}

执行输出

1
2
3
4
5
6
7
8
9
10
[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 .

硬编码示例

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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);
}
}

多规则示例

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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);
}

}

执行输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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相关打印.

参考文档