暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

easy-rules系列——EL表达式

weiyanweiyu 2021-06-24
3731

easy-rules仓库地址,开源协议是MIT。

本文是基于easy-rules的4.0.0版本。

作者:weiyiysw

邮箱:weiyiysw@163.com

本文章采用CC BY-NC-ND 4.0 许可协议进行许可

回顾

上一篇分析了使用注解及继承接口实现的规则,如何被实例化为Rule
接口。这一篇,我们来分析如何使用EL表达式实现规则定义以及实例化。

开篇和上篇一样,从一个shop示例开始。

shop示例

shop示例的逻辑如下:

  1. 顾客年龄是否大于18岁

  2. 大于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步:

  1. 构建事实对象

  2. 创建规则,并添加到规则集

  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文件里的规则。

  1. 创建MVELRuleFactory
    对象,Reader为YamlRuleDefinitionReader

  2. 文件路径

  3. 读取文件,并使用工厂对象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

文章转载自weiyanweiyu,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论