Easy-Rules规则引擎
使用情景:
当代码需要多次进行if/else
硬编码的时候,会导致代码的可读性大大降低,后期维护的成本增高。所以引入规则引擎,easy-rules是一个简单但是功能强大的规则引擎,提供了以下特性:
- 轻量级框架和易学习的API
- 基于POJO 的开发
- 支持从原始规则进行规则的组合
- 支持表达式(MVEL,SPEL和JEXL)定义规则
主要的接口:
Rules(规则接口),Facts(事实接口),RulesEngine(规则引擎接口)
快速开始
下面使用POJO 的方式进行示例
想了MVEL等方式定义的实例转到: https://segmentfault.com/a/1190000022939252
官方文档的GitHub地址:https://github.com/j-easy/easy-rules/wiki/defining-rules
1. 导入依赖
<!-- 导入此依赖只能使用注解形式进行规则创建及添加 -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>4.1.0</version>
</dependency>
<!--使用mvel和spel进行配置的时候需要导入的依赖-->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-mvel</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-spel</artifactId>
<version>3.4.0</version>
</dependency>
2. 规则编写
**规则一:**能被3整除的数
@Rule(name = "被3整除", description = "number如果被3整除,打印:number is three", priority = 1)
public class ThreeRule {
/**
* Condition:条件判断注解:如果return true, 执行Action
* @param number
*/
@Condition
public boolean isThree(@Fact("number") int number) {
return number % 3 == 0;
}
// Action 执行方法注解
@Action
public void threeAction(@Fact("number") int number) {
System.out.println(number + " is three");
}
}
**规则二:**能被8整除
@Rule(name = "被8整除", priority = 1)
public class EightRule {
@Condition
public boolean isEight(@Fact("number") int number) {
return number % 8 == 0;
}
@Action
public void eightAction(@Fact("number") int number) {
System.out.println(number + " is eight");
}
}
**组合规则:**既能被8整除又能被3整除
@Rule(name = "被3和8同时整除", description = "这是一个组合规则", priority = 0)
public class ThreeEightRuleUnitGroup extends UnitRuleGroup {
// 传入其他规则进行组合
public ThreeEightRuleUnitGroup(Object... rules) {
for (Object rule : rules) {
addRule(rule);
}
}
}
3. 初始化规则引擎,注册规则
class RulesTest {
@Test
public void test() {
// 初始化规则引擎
RulesEngineParameters parameters = new RulesEngineParameters()
.skipOnFirstAppliedRule(true);
DefaultRulesEngine engine = new DefaultRulesEngine(parameters);
// 注册规则进入引擎
Rules rules = new Rules();
rules.register(new ThreeRule());
rules.register(new EightRule());
rules.register(new ThreeEightRuleUnitGroup(new ThreeRule(), new EightRule()));
Facts facts = new Facts();
facts.put("number", 24);
// 开始判断
engine.fire(rules, facts);
}
}
Rule(规则)
可以把规则理解为if
语句和满足条件后的执行体,当**@Condition注解的方法返回真的时候则执行@Action注解的**方法
1. Annotation
-
@Rule:写在Rule类上,标识这是一个规则,可选参数
name
(规则命名空间的唯一规则名称),description
(描述规则的作用),priority
(设置规则的优先级,值越低优先级越高,不设置默认为**-1**,可以通过这个设置默认的响应规则) - @Condition: 写在方法上作为判断条件,为真的时候返回True并执行**@Action方法,返回False**的时候则直接跳过
2. Rule定义
使用注解定义Rule
@Rule(name = "my rule", description = "my rule description", priority = 1)
public class MyRule {
@Condition
public boolean when(@Fact("fact") fact) {
// 规则条件
return true;
}
@Action(order = 1)
public void then(Facts facts) throws Exception {
// 规则为true时的操作1
}
@Action(order = 2)
public void finally() throws Exception {
// 规则为true时的操作2
}
}
使用RuleBuilder定义规则
Rule rule = new RuleBuilder()
.name("myRule")
.description("myRuleDescription")
.priority(3)
.when(condition) // condition是Condition接口的实例对象
.then(action1) // action是Aciton接口的实例对象
.then(action2)
.build();
3. 组合规则
可以用前面基础的Rule规则自由组合一个更加复杂的规则
组合规则的分类
-
UnitRuleGroup
: 是作为一个单元使用的组合规则,要么所有规则都应用,要么都不用 -
ActivationRuleGroup
:激活规则组中触发第一个使用规则并忽略其他规则。规则首先按照在组中的自然顺序(默认情况下优先级)进行排序 -
ConditionalRuleGroup
: 条件规则组将具有最高优先级的规则作为条件,如果具有最高优先级的规则的计算结果为true,那么将触发其余的规则
使用UnitRuleGroup定义
// 从两个原始规则创建组合规则
UnitRuleGroup myUnitRuleGroup = new ThreeEightRuleUnitGroup("ThreeRule", "EightRule");
// 像常规规则一样注册组合规则
Rules rules = new Rules();
rules.register(myUnitRuleGroup);
RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, someFacts);
// 定义的组合规则类
@Rule(name = "被3和8同时整除", description = "这是一个组合规则")
public class ThreeEightRuleUnitGroup extends UnitRuleGroup {
public ThreeEightRuleUnitGroup(Object... rules) {
for (Object rule : rules) {
addRule(rule);
}
}
@Override
public int getPriority() {
return 0;
}
}
Facts(事实)
事实在我的理解就是为Rule规则提供类似反射一样的判断情况,如果没有Fact那么Rule的判断和普通的if/else
没有区别,还是一种硬编码的判断。当加入了Facts的时候,我们可以自定义丰富的Fact事实去进行多元的判断,修改判断逻辑的时候可以不修改Rule就能实现
1. fact的组成
public class Fact<T> {
private final String name;
private final T value;
}
fact
必须拥有两个值,一个名称和一个值。两个都不能为null
,name代表的是事实的命名空间,会根据name发送给Rule中Fact
相同的进行判断。value则可以携带判断信息和执行的方法。
**注:**可以自定义一个类,用来将判断的条件和判读后执行的方法都集成到里面去,减少代码的冗余
代码案例如下:
Facts facts = new Facts();
facts.put("wheather", "rain"); // 事实会被注入到对应的Rule中去
@Rule
class WeatherRule {
@Condition
public boolean itRains(@Fact("wheather") String wheather) {
return "rain".equals(wheather);
}
@Action
public void takeAnUmbrella(@Fact("wheather") String wheather) {
System.out.println("It rains, take an umbrella!");
}
}
注:
- 如果条件方法中缺少注入的事实,引擎将记录一个警告,并认为条件被计算为
false
。 - 如果动作方法中缺少注入的事实,则不会执行该动作,并且抛出
org.jeasy.rules.core.NoSuchFactException
异常。
RulesEngine
进行规则和事实进行判断的启动入口
1. RulesEngine类型:
-
DefaultRulesEngine
:根据规则的自然顺序(默认为优先级)应用规则。 -
InferenceRulesEngine
:在已知的事实上不断地应用规则,直到没有更多的规则可用
2. 规则引擎的参数
参数 | 类型 | 默认值 |
---|---|---|
rulePriorityThreshold | int | MaxInt |
skipOnFirstAppliedRule | boolean | false |
rulePriorityThreshold | int | false |
skipOnFirstFailedRule | boolean | false |
skipOnFirstNonTriggeredRule | boolean | false |
-
skipOnFirstAppliedRule
:当一个规则成功应用时,跳过余下的规则。 -
skipOnFirstFailedRule
:当一个规则失败时,跳过余下的规则。 -
skipOnFirstNonTriggeredRule
:当一个规则未触发时,跳过余下的规则。 -
rulePriorityThreshold
:当优先级超过指定的阈值时,跳过余下的规则。
3. 规则引擎的创建
// 1.先设置规则引擎的参数RulesEngineParameters parameters = new RulesEngineParameters()
RulesEngineParameters parameters = new RulesEngineParameters()
.rulePriorityThreshold(10)
.skipOnFirstAppliedRule(true)
.skipOnFirstFailedRule(true)
.skipOnFirstNonTriggeredRule(true);
// 2. 将引擎参数添加到规则引擎中去,进行规则引擎的初始化
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
Listener
1. 定义规则监听器
如果想对规则进行监听,需要自定义个一个类去实现这个接口然后注册到规则引擎当中去文章来源:https://www.toymoban.com/news/detail-770856.html
public interface RuleListener {
/**
* Triggered before the evaluation of a rule.
*
* @param rule being evaluated
* @param facts known before evaluating the rule
* @return true if the rule should be evaluated, false otherwise
*/
default boolean beforeEvaluate(Rule rule, Facts facts) {
return true;
}
/**
* Triggered after the evaluation of a rule.
*/
default void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) { }
/**
* Triggered on condition evaluation error due to any runtime exception.
*/
default void onEvaluationError(Rule rule, Facts facts, Exception exception) { }
/**
* Triggered before the execution of a rule.
*/
default void beforeExecute(Rule rule, Facts facts) { }
/**
* Triggered after a rule has been executed successfully.
*/
default void onSuccess(Rule rule, Facts facts) { }
/**
* Triggered after a rule has failed.
*/
default void onFailure(Rule rule, Facts facts, Exception exception) { }
}
2. 定义规则引擎监听器
public interface RulesEngineListener {
/**
* @param rules to fire
* @param facts present before firing rules
*/
default void beforeEvaluate(Rules rules, Facts facts) { }
/**
* @param rules fired
* @param facts present after firing rules
*/
default void afterExecute(Rules rules, Facts facts) { }
}
3. 使用例子
创建一个规则监听器的例子,规则引擎的类似文章来源地址https://www.toymoban.com/news/detail-770856.html
public class MyRuleListener implements RuleListener {
@Override
public boolean beforeEvaluate(Rule rule, Facts facts) {
System.out.println(rule.getName() + "is going to evaluate !!");
return true;
}
@Override
public void beforeExecute(Rule rule, Facts facts) {
System.out.println(rule.getName() + "is going to execute !");
}
}
// 注册到规则引擎中去
DefaultRulesEngine engine = new DefaultRulesEngine();
engine.registerRuleListener(new MyRuleListener());
到了这里,关于Easy-Rules引擎的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!