序
本文主要研究下easy-rules。
easy-rules是一款輕量級的規(guī)則引擎。
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創(chuàng)建方式
基于mvel表達(dá)式
easy-rules首先集成了mvel表達(dá)式捂襟,后續(xù)可能集成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\");"
- 加載運(yùn)行
//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可以標(biāo)注name和description屬性暖呕,每個rule的name要唯一哲泊,如果沒有指定堪侯,則RuleProxy則默認(rèn)取類名
- @Condition是條件判斷埃碱,要求返回boolean值猖辫,表示是否滿足條件
- @Action標(biāo)注條件成立之后觸發(fā)的方法
- @Priority標(biāo)注該rule的優(yōu)先級,默認(rèn)是Integer.MAX_VALUE - 1砚殿,值越小越優(yōu)先
實(shí)現(xiàn)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;
}
實(shí)現(xiàn)這個接口啃憎,也是創(chuàng)建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;
}
可以看到似炎,如果有實(shí)現(xiàn)Rule接口辛萍,則直接返回,沒有的話(
即基于注解的形式
)羡藐,則利用JDK的動態(tài)代理進(jìn)行包裝贩毕。
- 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對方法進(jìn)行了適配
下面以getName為例看下如何根據(jù)注解來返回
private String getRuleName() {
org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
return rule.name().equals(Rule.DEFAULT_NAME) ? getTargetClass().getSimpleName() : rule.name();
}
可以看到這里對注解進(jìn)行了解析
小結(jié)
從本質(zhì)上看,規(guī)則引擎的目的就是要以松散靈活的方式來替代硬編碼式的if else判斷仆嗦,來達(dá)到解耦的目的辉阶,不過實(shí)際場景要額外注意規(guī)則表達(dá)式的安全問題。