规则引擎有许多种:Drools,Aviator,Mvel,EasyRule,这里主要讲解一下EasyRule。
easyRule集成了Mvel,而Mvel表达式非常强大,我们可以自己写一些表达式,交给mvel进行解析计算,得到这个表达式计算的值。
easyRule可大致分为三部分:规则引擎,事实,规则
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-mvel</artifactId>
<version>4.1.0</version>
</dependency>
它有两种使用方式:一种是注解,一种是api方式,这里主要介绍一下api的方式
- 规则引擎
RulesEngine rulesEngine = new DefaultRulesEngine(); rulesEngine.fire(rules, facts);
从版本3.1开始,Easy Rules提供了RulesEngine接口的两种实现:
DefaultRulesEngine:根据规则的自然顺序(默认为优先级)应用规则。
InferenceRulesEngine:持续对已知事实应用规则,直到不再应用规则为止
什么意思呢?来看一段代码
public static void main(String[] args) { //规则条件 String condition = "name contains(\"张\")"; String condition2 = "age > 10 && age <= 20"; //规则引擎 RulesEngine rulesEngine = new DefaultRulesEngine(); Rules rules = new Rules(); //具体规则,当满足condition,然后输出对应的success Rule rule = new MVELRule().when(condition).then("System.out.println(\"name success\")").priority(2); Rule rule2 = new MVELRule().when(condition2).then("System.out.println(\"age success\")").priority(1); rules.register(rule); rules.register(rule2); //匹配规则的事实 Facts facts = new Facts(); facts.put("name","张"); facts.put("age","11"); rulesEngine.fire(rules,facts); }
使用DefaultRulesEngine引擎会根据规则设置的priority属性,从小到大顺序执行。
可以看到age先执行,name后执行。
使用InferenceRulesEngine引擎,会通过循环不停的通过事实去匹配规则,直到全部事实都不匹配规则才停止循环。可以看到源码中就是通过do while循环的
规则引擎也有几个参数默认都为false
- skipOnFirstAppliedRule:告诉引擎规则被触发时跳过后面的规则。
- skipOnFirstFailedRule:告诉引擎在规则失败时跳过后面的规则。
- skipOnFirstNonTriggeredRule:告诉引擎一个规则不会被触发跳过后面的规则。
- rulePriorityThreshold:告诉引擎如果优先级超过定义的阈值,则跳过下一个规则。版本3.3已经不支持更改,默认MaxInt。
通过构造函数设置进去就行了
RulesEngineParameters parameters = new RulesEngineParameters()
.rulePriorityThreshold(10)
.skipOnFirstAppliedRule(true)
.skipOnFirstFailedRule(true)
.skipOnFirstNonTriggeredRule(true);
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
- 事实
事实如何理解呢?规定定义好了,为谁定义的呢?就是Facts ,规则引擎就是判断这些Facts符不符合规则。在内部,Facts实例持有HashSet<Fact<?>>,这意味着:
1.事实需要命名,应该有一个唯一的名称,且不能为空
2. 任何Java对象都可以充当事实
不好理解来看代码
//可以这样
Facts facts = new Facts();
facts.put("name","张");
facts.put("age","11");
//也可以这样存放对象
Facts facts = new Facts();
Result p1 = new Result();
p1.setAge(11); p1.setName("张三");
Result p2 = new Result();
p2.setAge(12); p2.setName("张四");
//facts的key相同,后put会覆盖前一个person,因为Facts是通过HastSet来存储,并以key区分
facts.put("person",p1);
facts.put("person",p2);
//如果是这样那么规则就必须person.xxx这样写
String condition = "person.name contains(\"张\")";
String condition2 = "person.age > 10 && person.age <= 20";
@Data
class Result {
private String name;
private Integer age;
}
为了解决上面出现的覆盖问题,可以考虑下面这样的实现
public static void main(String[] args) {
List<Result> list = new ArrayList<>();
Result p1 = new Result();
p1.setAge(11);
p1.setName("张三");
Result p2 = new Result();
p2.setAge(12);
p2.setName("张四");
list.add(p1);
list.add(p2);
String condition = "person.name contains(\"张\")";
String condition2 = "person.age > 10 && person.age <= 20";
RulesEngine rulesEngine = new DefaultRulesEngine();
Rules rules = new Rules();
Rule rule = new MVELRule().when(condition).then("System.out.println(\"name success\")").priority(2);
Rule rule2 = new MVELRule().when(condition2).then("System.out.println(\"age success\")").priority(1);
rules.register(rule);
rules.register(rule2);
for (int i = 0; i < list.size(); i++) {
Facts facts = new Facts();
facts.put("person", list.get(i));
rulesEngine.fire(rules, facts);
}
}
- 规则
上面的案例都是一个事实匹配一个规则(一个person只要满足name包含张或者age >10 && age <20其中一个条件就执行对应规则校验成功后的逻辑),那如何做到一个事实匹配多个规则呢?也就是说一个person不仅要满足name包含张且age >10 && age <20才执行成功后的逻辑,就需要使用复合规则。
UnitRuleGroup:单元规则组是作为一个单元的复合规则:要么应用所有规则,要么什么都不应用。
ActivationRuleGroup:激活规则组是一个复合规则,它触发第一个适用规则,并忽略组中的其他规则(XOR逻辑)。规则首先按照组内的自然顺序(默认优先级)进行排序。
ConditionalRuleGroup:条件规则组是一个复合规则,其中优先级最高的规则充当条件:如果优先级最高的规则求值为true,则触发其余规则。
废话不多说,上demo
public static void main(String[] args) {
List<Result> list = new ArrayList<>();
Result p1 = new Result();
p1.setAge(11);
p1.setName("张三");
Result p2 = new Result();
p2.setAge(12);
p2.setName("李四");
list.add(p1);
list.add(p2);
String condition = "person.name contains(\"张\")";
String condition2 = "person.age > 10 && person.age <= 20";
RulesEngine rulesEngine = new DefaultRulesEngine();
Rules rules = new Rules();
Rule rule = new MVELRule().when(condition).then("System.out.println(\"name success\")").priority(2);
Rule rule2 = new MVELRule().when(condition2).then("System.out.println(\"age success\")").priority(1);
// 要么应用所有规则,要么什么都不应用。
UnitRuleGroup unitRuleGroup = new UnitRuleGroup();
unitRuleGroup.addRule(rule); unitRuleGroup.addRule(rule2);
rules.register(unitRuleGroup);
for (int i = 0; i < list.size(); i++) {
Facts facts = new Facts();
facts.put("person", list.get(i));
rulesEngine.fire(rules, facts);
}
}
输出结果:
可以看到 李四没有满足条件 ,其他几个复合规则,大家可自行去尝试。
以上就是easyRule的总结
参考文章:
https://zhuanlan.zhihu.com/p/91158525
https://www.jianshu.com/p/4eb762b559fd