序
本文主要研究下easy-rules。
easy-rules是一款轻量级的规则引擎。
maven
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-mvel</artifactId>
<version>3.1.0</version>
</dependency>
Rule创建方式
基于mvel表达式
easy-rules首先集成了mvel表达式,后续可能集成SpEL
- 配置文件
name: "alcohol rule"
description: "children are not allowed to buy alcohol"
priority: 2
condition: "person.isAdult() == false"
actions:
- "System.out.println(\"Shop: Sorry, you are not allowed to buy alcohol\");"
- 加载运行
//create a person instance (fact)
Person tom = new Person("Tom", 14);
Facts facts = new Facts();
facts.put("person", tom);
MVELRule alcoholRule = MVELRuleFactory.createRuleFrom(new File(getClass().getClassLoader().getResource("alcohol-rule.yml").getFile()));
// create a rule set
Rules rules = new Rules();
rules.register(alcoholRule);
//create a default rules engine and fire rules on known facts
RulesEngine rulesEngine = new DefaultRulesEngine();
System.out.println("Tom: Hi! can I have some Vodka please?");
rulesEngine.fire(rules, facts);
注解方式
@Rule
public class BuzzRule {
@Condition
public boolean isBuzz(@Fact("number") Integer number) {
return number % 7 == 0;
}
@Action
public void printBuzz() {
System.out.println("buzz");
}
@Priority
public int getPriority() {
return 2;
}
}
- @Rule可以标注name和description属性,每个rule的name要唯一,如果没有指定,则RuleProxy则默认取类名
- @Condition是条件判断,要求返回boolean值,表示是否满足条件
- @Action标注条件成立之后触发的方法
- @Priority标注该rule的优先级,默认是Integer.MAX_VALUE - 1,值越小越优先
实现Rule接口
easy-rules-core-3.1.0-sources.jar!/org/jeasy/rules/api/Rule.java
/**
* Abstraction for a rule that can be fired by the rules engine.
*
* Rules are registered in a rule set of type <code>Rules</code> in which they must have a <strong>unique</strong> name.
*
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*/
public interface Rule extends Comparable<Rule> {
/**
* Default rule name.
*/
String DEFAULT_NAME = "rule";
/**
* Default rule description.
*/
String DEFAULT_DESCRIPTION = "description";
/**
* Default rule priority.
*/
int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;
/**
* Getter for rule name.
* @return the rule name
*/
String getName();
/**
* Getter for rule description.
* @return rule description
*/
String getDescription();
/**
* Getter for rule priority.
* @return rule priority
*/
int getPriority();
/**
* Rule conditions abstraction : this method encapsulates the rule's conditions.
* <strong>Implementations should handle any runtime exception and return true/false accordingly</strong>
*
* @return true if the rule should be applied given the provided facts, false otherwise
*/
boolean evaluate(Facts facts);
/**
* Rule actions abstraction : this method encapsulates the rule's actions.
* @throws Exception thrown if an exception occurs during actions performing
*/
void execute(Facts facts) throws Exception;
}
实现这个接口,也是创建rule的一种形式。
源码解析
- register
easy-rules-core-3.1.0-sources.jar!/org/jeasy/rules/api/Rules.java
/**
* Register a new rule.
*
* @param rule to register
*/
public void register(Object rule) {
Objects.requireNonNull(rule);
rules.add(RuleProxy.asRule(rule));
}
这里使用RuleProxy.asRule方法
- RuleProxy
easy-rules-core-3.1.0-sources.jar!/org/jeasy/rules/core/RuleProxy.java
/**
* Makes the rule object implement the {@link Rule} interface.
*
* @param rule the annotated rule object.
* @return a proxy that implements the {@link Rule} interface.
*/
public static Rule asRule(final Object rule) {
Rule result;
if (rule instanceof Rule) {
result = (Rule) rule;
} else {
ruleDefinitionValidator.validateRuleDefinition(rule);
result = (Rule) Proxy.newProxyInstance(
Rule.class.getClassLoader(),
new Class[]{Rule.class, Comparable.class},
new RuleProxy(rule));
}
return result;
}
可以看到,如果有实现Rule接口,则直接返回,没有的话(
即基于注解的形式
),则利用JDK的动态代理进行包装。
- invoke
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
String methodName = method.getName();
switch (methodName) {
case "getName":
return getRuleName();
case "getDescription":
return getRuleDescription();
case "getPriority":
return getRulePriority();
case "compareTo":
return compareToMethod(args);
case "evaluate":
return evaluateMethod(args);
case "execute":
return executeMethod(args);
case "equals":
return equalsMethod(args);
case "hashCode":
return hashCodeMethod();
case "toString":
return toStringMethod();
default:
return null;
}
}
可以看到这里invoke对方法进行了适配
下面以getName为例看下如何根据注解来返回
private String getRuleName() {
org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
return rule.name().equals(Rule.DEFAULT_NAME) ? getTargetClass().getSimpleName() : rule.name();
}
可以看到这里对注解进行了解析
小结
从本质上看,规则引擎的目的就是要以松散灵活的方式来替代硬编码式的if else判断,来达到解耦的目的,不过实际场景要额外注意规则表达式的安全问题。