规则引擎使用简介(easyRules)
github地址:https://github.com/j-easy/easy-rules
参考文章:https://www.jianshu.com/p/9b67ab434795
https://my.oschina.net/crosschen/blog/4803180
http://t.zoukankan.com/rongfengliang-p-12693489.html
文章目录
为什么要用规则引擎替代if-else?
世界上最遥远的距离,是我在if里你在else里
为什么会使用到if-else?
- 刚开始自己写的代码很简洁,逻辑清晰,函数精简,没有一个if-else
但是需求发展却不以人的意志为转移 - 业务逻辑千奇百怪
- 需求无限叠加
- 项目进度约束
- …
由于以上诸多原因,最终代码中会出现很多的if-else,而且更有甚者有很多嵌套的if-else,着会给接锅者头越来越秃
使用if-else的场景主要有
- 异常逻辑处理
- 特殊case处理
- 不同状态处理
过多的繁琐的if-else不但会降低程序的性能,还不利于后期维护,有可能出现昨天写的代码,今天来看“我靠!这是啥呀!!”
现在有一个相对较好的工具可以帮助我们进行复杂逻辑处理,那就是规则引擎。
什么是规则引擎
如果是简单的逻辑判断可以通过if-else轻松解决,但是,现实中的业务逻辑不允许我们轻松解决,一旦业务逻辑复杂了,if-else接那么香了,规则引擎就不同,你可以根据业务场景来编写不同的规则,即使业务变更了,只需要修改或添加相关的规则就可,然后引用不同的规则进行逻辑处理,这样做可以极大的降低开发成本和运维的难度,也变相保护了大伙的头发,何乐而不为呢?!
规则引擎应用场景
- 流程分支非常复杂,规则变量庞大,常规编码(if-else)难以实现
- 有不确定性的需求,变更频率较高
- 需要快速做出响应和决策
- 规则变更期望脱离于开发人员,脱离coding
规则引擎流程
业务规则发展历程
相关工具
Drools, easy-rules,Rule Book(未了解过)
Drools | easy-rules | Rule Book | |
---|---|---|---|
简介 | 可以执行复杂事件处理的规则引擎 | 简单轻量级的规则引擎 | 支持Lambda的轻量级规则引擎 |
流行度 | 8.7 ★★★★★ | 8.0 ★★★★ | 5.1 ★★★ |
活跃度 | 9.4 ❤❤❤❤❤ | 7.5 ❤❤❤❤ | 4.4 ❤❤❤ |
代码质量 | L1(Lumnify统计) | L5(Lumnify统计) | |
当前版本 | 7.47.0.Final | V 4.1 | V 0.12 |
版本支持 | 7.x 版本已持续多年 | 仅支持 4.1.x,老版本不支持更新 | 2018年5月发布V 0.11,V 0.12发布时间不详 |
编程语言 | Java | Java | Java |
算法 | RETE算法 | - | - |
easy-rules使用简介
相关依赖:
<dependencies>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-support</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-mvel</artifactId>
<version>4.1.0</version>
</dependency>
</dependencies>
规则定义
-
基于注解的javaBean定义
使用@Rule来定义一个规则,@Condition 申明规则条件判断(只能有一个),@Action 申明规则执行内容(可有多个,并且可以通过order指定优先级)
@Rule(name = "weather rule", description = "if it rains then take an umbrella") class WeatherRule { @Condition public boolean itRains(@Fact("rain") boolean rain) { return rain; } @Action(order = 0) public void takeAnUmbrella() { System.out.println("It rains, take an umbrella!"); } @Action(order = 1) public void takeAnUmbrella() { System.out.println("It rains, take an umbrella2!"); } }
测试样例:
public static void main(String[] args) { Facts facts = new Facts(); facts.put("rain", true); // define rules Rules rules = new Rules(); rules.register(new WeatherRules()); // fire rules on known facts RulesEngine rulesEngine = new DefaultRulesEngine(); rulesEngine.fire(rules, facts); }
-
流式编程方式实现
通过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();
测试代码:
public class FluentTest { public static void main(String[] args) { 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! \nThat's fluentTest!\");"); Rules rules = new Rules(); rules.register(weatherRule); Facts facts = new Facts(); facts.put("rain", true); RulesEngine engine = new DefaultRulesEngine(); engine.fire(rules, facts); } }
-
表达式定义
通过MVELRule类来定义一个规则,通过表达是的方式在规则类上定义规则内容
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!\");");
测试代码:
public class ExpressionTest { public static void main(String[] args) { 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! \nThat's ExpressionTest\");"); Rules rules = new Rules(); rules.register(weatherRule); Facts facts = new Facts(); facts.put("rain", true); RulesEngine engine = new DefaultRulesEngine(); engine.fire(rules, facts); } }
-
yml文件定义
通过定义一个yml规则文件,在通过读取文件来获取规则
通过MVELRuleFactory类来获取yml文件定义的规则
需要添加依赖
<dependency> <groupId>org.jeasy</groupId> <artifactId>easy-rules-mvel</artifactId> <version>4.1.0</version> </dependency>
yml文件:
文件路径:resource/rules/rain.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("rain.yml"));
测试代码:
public class YmlTest { public static void main(String[] args) { MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader()); // 读取规则文件 String path = YmlTest.class.getResource("/rules/rain.yml").getPath(); try { FileReader ymlReader = new FileReader(path); Rule rule = ruleFactory.createRule(ymlReader); Rules rules = new Rules(); rules.register(rule); Facts facts = new Facts(); facts.put("rain", true); RulesEngine engine = new DefaultRulesEngine(); engine.fire(rules, facts); }catch (Exception e) { e.printStackTrace(); } } }
-
json文件定义
通过定义一个json规则文件,在通过读取文件来获取规则
通过MVELRuleFactory类来获取json文件定义的规则
需要添加依赖(若需要用到UnitRuleGroup相关类则需要添加第二个依赖)
<dependency> <groupId>org.jeasy</groupId> <artifactId>easy-rules-mvel</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>org.jeasy</groupId> <artifactId>easy-rules-support</artifactId> <version>4.1.0</version> </dependency>
json文件:
//单个规则 [ { "name": "3", "description": "if it rains then take an umbrella", "condition": "rain == true", "priority": 3, "actions": [ "System.out.println(\"It rains, take an umbrella!\")" ] } ] // 多个规则 [ { "name": "1", "description": "1", "priority": 1, "compositeRuleType": "UnitRuleGroup", "composingRules": [ { "name": "2", "description": "2", "condition": "user.getAge()<28", "priority": 2, "actions": [ "System.out.println(\"UnitRuleGroup not ok rule2\")" ] }, { "name": "3", "description": "3", "condition": "user.name.length<10", "priority": 3, "actions": [ "System.out.println(\"UnitRuleGroup rule3\")" ] }, { "name": "4", "description": "4", "condition": "user.name.length<10", "priority": 4, "actions": [ "System.out.println(\"UnitRuleGrouprule4\")" ] }, { "name": "5", "description": "5", "condition": "user.name.length<10", "priority": 5, "actions": [ "System.out.println(\"UnitRuleGrouprule5\"), UserService.doAction4(user.userinfo)" ] } ] }, { "name": "3", "description": "3", "condition": "user.name.length<50", "priority": 3, "actions": [ "System.out.println(\"defaultrule3\")" ] } ]
文件读取:
MVELRuleFactory jsonFactory = new MVELRuleFactory(new JsonRuleDefinitionReader()); Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.json"));
测试代码:
public static void main(String[] args) { MVELRuleFactory factory = new MVELRuleFactory(new JsonRuleDefinitionReader()); // 读取json文件 String path = JsonTest.class.getResource("/rules/rain.json").getPath(); try { FileReader jsonFile = new FileReader(path); Rule rule = factory.createRule(jsonFile); Rules rules = new Rules(); rules.register(rule); Facts facts = new Facts(); facts.put("rain", true); RulesEngine engine = new DefaultRulesEngine(); engine.fire(rules, facts); } catch (Exception e) { e.printStackTrace(); } }
读取json文件时报了个错,我理解因该是要将json对象构造为数据结构,就算是单个规则也要已数据结构存放才行,我把json文件中的内容使用‘[ ]’ 框起来就好了。
执行规则大致流程:
public class Test {
public static void main(String[] args) {
// define facts
Facts facts = new Facts();
facts.put("rain", true);
// define rules 构造rule的形式有上面列举的几种
Rule weatherRule = ...
Rules rules = new Rules();
rules.register(weatherRule);
// fire rules on known facts
RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, facts);
}
}
执行引擎(engine)
easyRules提供了两种引擎,分别是DefaultRulesEngine和InferenceRulesEngine
DefaultRulesEngine:根据设置的priority 来执行规则,若不设置priority则根据默认的顺序执行,每个规则只执行一次
InferenceRulesEngine:循环执行规则,只有当所有规则无法匹配成功才结束匹配(这里会出现死循环)
在对InferenceRulesEngine做测试时,我想每次在Condition中修改Face对象,但是并没有找到操作的方法,想了一下,condition中仅仅只是做判断的,好像不具备逻辑处理的能力,我觉得这是合理的,如果condition中做逻辑处理就违背了它的本职,最多也只能调用外部方法进行逻辑判断,而不是直接进行逻辑处理,逻辑处理应该要交到Action中进行。
引擎参数
参数 | 类型 | 是否必填 | 默认值 | 描述 |
---|---|---|---|---|
rulePriorityThreshold | int | no | MaxInt | 当规则的priority超过设置的阈值,跳过规则 |
skipOnFirstAppliedRule | boolean | no | false | 当第一个规则执行成功时,跳过下一个规则 |
skipOnFirstFailedRule | boolean | no | false | 当第一个规则执行失败时,跳过下一个规则 |
skipOnFirstNonTriggeredRule | boolean | no | false | 当第一个规则未触发时,跳过下一个规则 |
测试:
-
skipOnFirstAppliedRule参数
@Rule(name = "fiveRules", description = "this is fiveRules") public class FiveRules { @Condition public boolean isFive(@Fact("number") Integer number){ return number % 5 == 0; } @Action public void isFizz(@Fact("number") Integer number){ System.out.println(number + "是5的倍数"); } @Priority public int getPriority(){ return 5; } }
@Rule(name = "sevenRules", description = "this is sevenRules") public class SevenRules { @Condition public boolean isSeven(@Fact("number") Integer number){ return number % 7 == 0; } @Action public void then(@Fact("number") Integer number){ System.out.println(number + "7的倍数!"); } @Priority public int getPriority(){ return 10; } }
Rules rules = new Rules(); rules.register(new SevenRules()); rules.register(new FiveRules()); // rules.register(new FiveOrSevenRules(new FiveRules(), new SevenRules())); // rules.register(new NonFiveOrSevenRules()); Facts facts = new Facts(); facts.put("number", 35); RulesEngineParameters parameters = new RulesEngineParameters(); parameters.setSkipOnFirstAppliedRule(true); RulesEngine engine = new DefaultRulesEngine(parameters);// 应用参数 RulesEngine engine1 = new DefaultRulesEngine();// 不应用参数 engine.fire(rules, facts); System.out.println("--------------------------"); engine1.fire(rules, facts); System.out.println("--------------------------");
运行结果: 35 35是5的倍数 -------------------------- 35 35是5的倍数 357的倍数! --------------------------
-
skipOnFirstFailedRule参数
这个参数有点没整明白,失败跳过下一个规则,我理解的应该是返回false则不会执行下一个规则,但是即便第一个规则返回了false,下一个规则还是被执行了;也尝试了抛异常然后就不执行下一个规则,但是就算抛了异常,下一个规则还是被执行了,所以我有点不太懂了,代码就不贴了
-
skipOnFirstNonTriggeredRule参数
这个和上面那个一样,没整明白!!!! -
rulePriorityThreshold参数
只有priority参数小于等于设置的值时规则才会被触发@Rule(name = "fiveRules", description = "this is fiveRules") public class FiveRules { @Condition public boolean isFive(@Fact("number") Integer number){ System.out.println("触发规则isFive"); return number % 5 == 0; } @Action public void isFizz(@Fact("number") Integer number){ System.out.println(number + "是5的倍数"); } @Priority public int getPriority(){ return 5; } }
@Rule(name = "sevenRules", description = "this is sevenRules") public class SevenRules { @Condition public boolean isSeven(@Fact("number") Integer number){ System.out.println("触发规则isSeven"); return number % 7 == 0; } @Action public void then(@Fact("number") Integer number){ System.out.println(number + "7的倍数!"); } @Priority public int getPriority(){ return 10; } }
public static void main(String[] args) { RulesEngine engine = new DefaultRulesEngine(parameters); Rules rules = new Rules(); rules.register(new SevenRules()); rules.register(new FiveRules()); Facts facts = new Facts(); facts.put("number", 7); RulesEngineParameters parameters = new RulesEngineParameters(); // parameters.setSkipOnFirstAppliedRule(true); // parameters.setSkipOnFirstFailedRule(true); // parameters.setSkipOnFirstNonTriggeredRule(true); parameters.setPriorityThreshold(5); RulesEngine engine = new DefaultRulesEngine(parameters); RulesEngine engine1 = new DefaultRulesEngine(); engine.fire(rules, facts); System.out.println("--------------------------"); engine1.fire(rules, facts); System.out.println("--------------------------"); }
运行结果: 触发规则isFive -------------------------- 触发规则isFive 触发规则isSeven 77的倍数! --------------------------
规则监听
触发方法名 | 触发时机 |
---|---|
beforeEvaluate | 在执行规则Condition之前执行 |
afterEvaluate | 在执行完规则Condition之后执行 |
onEvaluationError | 在执行规则Condition中发生异常后执行 |
beforeExecute | 在执行规则Action之前执行 |
onSuccess | 在执行规则Action完成后执行 |
onFailure | 在执行规则Action发生异常之后执行 |
监听类:
public class MyRules implements RuleListener {
@Override
public boolean beforeEvaluate(Rule rule, Facts facts) {
System.out.println("beforeEvaluate 触发");
return true;
}
@Override
public void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) {
System.out.println("afterEvaluate 触发");
}
@Override
public void onEvaluationError(Rule rule, Facts facts, Exception exception) {
System.out.println("onEvaluationError 触发");
}
@Override
public void beforeExecute(Rule rule, Facts facts) {
System.out.println("beforeExecute 触发");
}
@Override
public void onSuccess(Rule rule, Facts facts) {
System.out.println("onSuccess 触发");
}
@Override
public void onFailure(Rule rule, Facts facts, Exception exception) {
System.out.println("onFailure 触发");
}
}
规则类FiveRules:
@Rule(name = "fiveRules", description = "this is fiveRules")
public class FiveRules {
@Condition
public boolean isFive(@Fact("number") Integer number){
System.out.println("触发规则isFive Condition");
// System.out.println(1/0);
return number % 5 == 0;
}
@Action
public void isFizz(@Fact("number") Integer number){
System.out.println(number + "是5的倍数 触发Action");
// System.out.println(1/0);
}
@Priority
public int getPriority(){
return 5;
}
}
规则类SevenRules:
@Rule(name = "sevenRules", description = "this is sevenRules")
public class SevenRules {
@Condition
public boolean isSeven(@Fact("number") Integer number){
System.out.println("触发规则isSeven Condition");
System.out.println(1/0);
return number % 7 == 0;
}
@Action
public void then(@Fact("number") Integer number){
System.out.println(number + " 是7的倍数!触发isSeven action");
}
@Priority
public int getPriority(){
return 10;
}
}
执行程序:
要使用监听需要使用DefaultRulesEngine 或 InferenceRulesEngine 规则引擎,使用一般的RulesEngine是没法使用监听功能的,监听器可以设置多个,若设置多个使用registerRuleListeners(List ruleListeners) 参数传入一组监听器即可。
public static void main(String[] args) {
Rules rules = new Rules();
rules.register(new SevenRules());
rules.register(new FiveRules());
Facts facts = new Facts();
facts.put("number", 35);
DefaultRulesEngine engine = new DefaultRulesEngine();
engine.registerRuleListener(new MyRules());
engine.fire(rules, facts);
}
当监听的规则是一组复合规则的时候,监听程序只会作用在这一组规则上,而不是作用于组内的每一个规则,也就是说一组股则所有成功了,才算成功,只要有一个失败过异常,都不会触发成功(onSuccess),有点类似数据库的事务。
符合规则类:
public class FiveOrSevenRules extends UnitRuleGroup{
public FiveOrSevenRules(Object... rules){
for (Object rule : rules) {
this.addRule(rule);
}
}
@Override
public int getPriority() {
return 0;
}
}
执行程序:
public static void main(String[] args) {
Rules rules = new Rules();
rules.register(new FiveOrSevenRules(new FiveRules(), new SevenRules()));
Facts facts = new Facts();
facts.put("number", 35);
DefaultRulesEngine engine = new DefaultRulesEngine();
engine.fire(rules, facts);
}
引擎监听
监听方法名 | 触发时机 |
---|---|
beforeEvaluate | 规则引擎触发之前执行,即在第一个规则的condition之前,先于规则监听的beforeEvaluate方法 |
afterExecute | 所有规则执行完成之后,后于规则监听的所有方法 |
引擎监听类:
引擎监听类需要实现 RulesEngineListener 类,通过重写beforeEvaluate方法和afterExecute方法实现
public class MyEngineListener implements RulesEngineListener {
@Override
public void beforeEvaluate(Rules rules, Facts facts) {
System.out.println("engine beforeEvaluate 触发");
}
@Override
public void afterExecute(Rules rules, Facts facts) {
System.out.println("engine afterExecute 触发");
}
}
规则类和上面规则监听的一样就不重复了
执行程序:
通过向规则引擎注册引擎监听类来进行监听,引擎监听也可以传多个,使用registerRulesEngineListeners(List rulesEngineListeners)方法,参数传入一组监听类即可。
public static void main(String[] args) {
Rules rules = new Rules();
rules.register(new FiveOrSevenRules(new FiveRules(), new SevenRules()));
Facts facts = new Facts();
facts.put("number", 35);
DefaultRulesEngine engine = new DefaultRulesEngine();
engine.registerRulesEngineListener(new MyEngineListener());
engine.registerRuleListener(new MyRules());
engine.fire(rules, facts);
}
表达式语言支持
这个在上面的规则定义已经有了,请移步规则定义
写了点测试代码,有兴趣的可以看一下,点这里查看
Drools
参考另一篇文章:https://blog.youkuaiyun.com/qq_41896365/article/details/122233006?spm=1001.2014.3001.5501