easy-rules仓库地址,开源协议是MIT。
本文是基于easy-rules的4.0.0版本。
作者:weiyiysw
邮箱:weiyiysw@163.com
本文章采用CC BY-NC-ND 4.0 许可协议进行许可
回顾
上一篇分析了使用注解及继承接口实现的规则,如何被实例化为Rule
接口。这一篇,我们来分析如何使用EL表达式实现规则定义以及实例化。
开篇和上篇一样,从一个shop示例开始。
shop示例
shop示例的逻辑如下:
顾客年龄是否大于18岁
大于18岁,则允许买酒,否则,不允许。
我们先来看下Lanucher
主函数:
public static void main(String[] args) throws Exception {
//create a person instance (fact)
Person tom = new Person("Tom", 14);
Facts facts = new Facts();
facts.put("person", tom);
// create rules
MVELRule ageRule = new MVELRule()
.name("age rule")
.description("Check if person's age is > 18 and mark the person as adult")
.priority(1)
.when("person.age > 18")
.then("person.setAdult(true);");
MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
String fileName = args.length != 0 ? args[0] : "easy-rules-tutorials/src/main/java/org/jeasy/rules/tutorials/shop/alcohol-rule.yml";
Rule alcoholRule = ruleFactory.createRule(new FileReader(fileName));
// create a rule set
Rules rules = new Rules();
rules.register(ageRule);
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);
}
规则引擎的用法,依然是分3步:
构建事实对象
创建规则,并添加到规则集
实例化规则引擎,执行
从源码上,可以看到,第二步和上篇是不一样的。这里创建了2个规则,ageRule
和alcoholRule
,2个规则分别用了不一样的写法,我们一个个来分析。
先来看下Person
类的定义,很简单,就不解释了。
public class Person {
private String name;
private int age;
private boolean adult;
// ... 省略getter setter toString方法
}
流式API定义的规则
第一个ageRule
规则是基于流式API风格定义的规则。
MVELRule ageRule = new MVELRule()
.name("age rule")
.description("Check if person's age is > 18 and mark the person as adult")
.priority(1)
.when("person.age > 18")
.then("person.setAdult(true);");
可以看到,一个规则,包含这些信息,冒号后是对应到的注解
规则名称:注解
Rule
的name
字段规则描述:注解
Rule
的description
字段优先级:注解
Priority
规则条件:注解
Condition
规则动作:注解
Action
可以看到,这里创建的是一个MVELRule
对象。我们先来看下MVELRule
对象的类图,继承自BasicRule
,因此它本身就是一个Rule
对象。
接下来,看它的实现,name
、description
、priority
三个方法都是常规的赋值。
public MVELRule name(String name) {
this.name = name;
return this;
}
public MVELRule description(String description) {
this.description = description;
return this;
}
public MVELRule priority(int priority) {
this.priority = priority;
return this;
}
重点,我们看下when
、then
方法,可以看到,这里引入了2个新的对象MVELCondition
与MVELAction
对象,在这2个对象里,就是使用MVEL的用法了。
// used to parse condition/action expressions
private final ParserContext parserContext;
public MVELRule() {
this(new ParserContext());
}
public MVELRule when(String condition) {
this.condition = new MVELCondition(condition, parserContext);
return this;
}
public MVELRule then(String action) {
this.actions.add(new MVELAction(action, parserContext));
return this;
}
MVELCondition
中被调用的方法
public MVELCondition(String expression, ParserContext parserContext) {
compiledExpression = MVEL.compileExpression(expression, parserContext);
}
MVELAction
中被调用的方法
public MVELAction(String expression, ParserContext parserContext) {
this.expression = expression;
compiledExpression = MVEL.compileExpression(expression, parserContext);
}
更多的MVEL的应用,大家可以去看MVEL的文档。
当规则引擎doFire
时,是执行的evaluate(Facts facts)
判断规则是否为true
,为true
则执行execute(Facts facts)
方法。
private Condition condition = Condition.FALSE;
private final List<Action> actions = new ArrayList<>();
@Override
public boolean evaluate(Facts facts) {
return condition.evaluate(facts);
}
@Override
public void execute(Facts facts) throws Exception {
for (Action action : actions) {
action.execute(facts);
}
}
可以看到,本质是调用MVELCondition
的evaluate(Facts facts)
方法与MVELAction
中的execute(Facts facts)
方法。
// MVELCondition.java
@Override
public boolean evaluate(Facts facts) {
// MVEL.evalToBoolean does not accept compiled expressions..
return (boolean) MVEL.executeExpression(compiledExpression, facts.asMap());
}
// MVELAction.java
@Override
public void execute(Facts facts) {
try {
MVEL.executeExpression(compiledExpression, facts.asMap());
} catch (Exception e) {
LOGGER.error("Unable to evaluate expression: '" + expression + "' on facts: " + facts, e);
throw e;
}
}
使用MVELRule
是通过MVEL
表达式来做条件计算及动作执行的。
YAML定义规则
alcohol规则是使用YAML
定义的(注意,easy-rules
也支持使用json
格式定义规则)。
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\");"
下面三行代码,用来创建YAML文件里的规则。
创建
MVELRuleFactory
对象,Reader为YamlRuleDefinitionReader
文件路径
读取文件,并使用工厂对象
createRule
。
MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
String fileName = args.length != 0 ? args[0] : "easy-rules-tutorials/src/main/java/org/jeasy/rules/tutorials/shop/alcohol-rule.yml";
Rule alcoholRule = ruleFactory.createRule(new FileReader(fileName));
从上2个类图,可以看到,内置支持YAML
与JSON
的reader,支持MVEL
与SpEL
。
接下来,看下createRule(Reader ruleDescriptor)
方法实现:
// 1. 调用 MVELRuleFactory.java 方法
public Rule createRule(Reader ruleDescriptor) throws Exception {
List<RuleDefinition> ruleDefinitions = reader.read(ruleDescriptor);
if (ruleDefinitions.isEmpty()) {
throw new IllegalArgumentException("rule descriptor is empty");
}
return createRule(ruleDefinitions.get(0));
}
// 2. AbstractRuleFactory.java 判断是否是组合规则
// 如果是组合规则,则执行抽象类中的createCompositeRule(RuleDefinition ruleDefinition)
// 方法,创建Rule
protected Rule createRule(RuleDefinition ruleDefinition) {
if (ruleDefinition.isCompositeRule()) {
return createCompositeRule(ruleDefinition);
} else {
return createSimpleRule(ruleDefinition);
}
}
// 3.1 如果是simpleRule,则进入到MVELRuleFactory或SpELRuleFactory里的实现
protected abstract Rule createSimpleRule(RuleDefinition ruleDefinition);
// 3.2 MVELRuleFactory.java 简单规则实现,可以看到,使用的是流式API方式创建MVELRule
protected Rule createSimpleRule(RuleDefinition ruleDefinition) {
MVELRule mvelRule = new MVELRule(parserContext)
.name(ruleDefinition.getName())
.description(ruleDefinition.getDescription())
.priority(ruleDefinition.getPriority())
.when(ruleDefinition.getCondition());
for (String action : ruleDefinition.getActions()) {
mvelRule.then(action);
}
return mvelRule;
}
// reader读取规则配置文件,将其实例化为RuleDefine对象,传入工厂类去实例化Rule
public class RuleDefinition {
private String name = Rule.DEFAULT_NAME;
private String description = Rule.DEFAULT_DESCRIPTION;
private int priority = Rule.DEFAULT_PRIORITY;
private String condition;
private List<String> actions = new ArrayList<>();
private List<RuleDefinition> composingRules = new ArrayList<>();
private String compositeRuleType;
// ... 省略getter setter方法
}
这里就不在赘述SpEL
了,二者在easy-rules
中的实现是一样的原理。
至此,EL表达式相关分析结束了。
组合规则
组合规则,其实就是一组简单规则的组合。在easy-rules
中有三种类型的组合规则。
ActivationRuleGroup:执行第一条表达式为
true
的简单规则(simpleRule)。ConditionalRuleGroup:最高优先级的第一个规则作为条件,第一个规则为
true
时,执行后续所有表达式为true
的规则。UnitRuleGroup:当前仅当组合内全部规则都为
true
才会执行。
结语
至此,easy-rules
系列就全部结束了。3篇文章,分析了整个项目的框架结构,以及规则执行逻辑。读者可以自行根据文章描述的项目整体框架,去详细看全部源码,加深理解。
下一期
下一篇文章计划:在非Spring
系列的Java
项目中,如何使用mybatis
,如何使用HikariCP
?