Aviator规则表达式引擎介绍及源码跟踪

Aviator

规则表达式引擎分类

  1. 编译型规则表达式引擎:预先编译成可执行对象,运行时多次执行;
  2. 解释型规则表达式引擎:不需要预先进行编译,在运行时,要先进行解释再运行。

Aviator原理及特点

Aviator 的基本过程是将字符串表达式直接翻译成对应的 java 字节码执行,整个过程最多扫两趟(开启执行优先模式,如果是编译优先模式下就一趟),这样就保证了它的性能超越绝大部分解释性的表达式引擎,测试也证明如此;其次,除了依赖 commons-beanutils 这个库之外(用于做反射)不依赖任何第三方库,因此整体非常轻量级,整个 jar 包大小哪怕发展到现在 5.0 这个大版本,也才 430K。同时, Aviator 内置的函数库非常“节制”,除了必须的字符串处理、数学函数和集合处理之外,类似文件 IO、网络等等你都是没法使用的,这样能保证运行期的安全,如果你需要这些高阶能力,可以通过开放的自定义函数来接入。因此总结它的特点是:

  • 高性能
  • 轻量级
  • 开放能力:包括自定义函数接入以及各种定制选项
  • 一些比较有特色的特点:
    1.支持运算符重载
    2.原生支持大整数和 BigDecimal 类型及运算,并且通过运算符重载和一般数字类型保持一致的运算方式。
    3.原生支持正则表达式类型及匹配运算符 =~
    4.支持lambda ,可以灵活地处理各种集合

Aviator解析

AviatorScript 编译和执行的入口是 AviatorEvaluatorInstance 类,该类的一个实例就是一个编译和执行的单元。

AviatorEvaluator.getInstance() 返回一个全局共享的AviatorEvaluatorInstance 类,没有定制化的需求,该默认引擎已足够我们本次的讲解。

AviatorEvaluatorInstance 入口常用Api:

public final class AviatorEvaluatorInstance {
  //编译字符串表达式,cacheKey缓存标识,cached-是否缓存
  public Expression compile(final String cacheKey, final String expression, final boolean cached)
  
  //编译aviatorScript脚本文件,cacheKey缓存标识,cached-是否缓存
  public Expression compileScript(final String cacheKey, final File file, final boolean cached) throws IOException
  
  //执行字符串表达式,env环境变量,cached-是否缓存Expression对象
  public Object execute(final String expression, final Map<String, Object> env,
      final boolean cached)      
}

示例1、执行字符串表达式

AviatorEvaluatorInstance aviatorEvaluatorInstance = AviatorEvaluator.newInstance();
//1.直接执行表达式
String exp1 = "1+2+3";
Object execute = aviatorEvaluatorInstance.execute(exp1);
System.out.println(execute);//6
String exp2 = "100>1000";
Object execute2 = aviatorEvaluatorInstance.execute(exp2);
System.out.println(execute2);//false
//2.使用环境变量
Map<String, Object> env = AviatorEvaluator.newEnv("a", 100, "b", 200, "c", 300);
String exp3 = "a+b+c";
Object execute3 = aviatorEvaluatorInstance.execute(exp3,env);
System.out.println(execute3);//600
String exp4 = "a+b==c";
Object execute4 = aviatorEvaluatorInstance.execute(exp4,env);
System.out.println(execute4);//false
//访问环境变量数组和集合
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
int[] nums = new int[5];
for(int i=0;i<nums.length;i++){
    nums[i]=i;
}
Map<String,Object> map2 = new HashMap<String,Object>();
map2.put("date","2021-08-21");
Map<String,Object> map3 = new HashMap<String,Object>();
map3.put("list",list);
map3.put("nums",nums);
map3.put("map2",map2);
//hello world
System.out.println(AviatorEvaluator.execute("list[0]+' '+list[1]",map3));
//nums[0]+nums[1]+nums[2]=3
System.out.println(AviatorEvaluator.execute("'nums[0]+nums[1]+nums[2]='+ (nums[0]+nums[1]+nums[2])",map3));
//当前时间为:2021-08-21
System.out.println(AviatorEvaluator.execute("'当前时间为:'+map2.date",map3));
}

示例2、执行/导入函数

AviatorEvaluatorInstance aviatorEvaluatorInstance = AviatorEvaluator.newInstance();
//1.使用自有函数
System.out.println(AviatorEvaluator.execute("string.substring('habcello',1,3)"));//ab
System.out.println(AviatorEvaluator.execute("string.contains(\"abc\",\"ab\")"));//true
//2.导入静态方法
aviatorEvaluatorInstance.addStaticFunctions("StringUtils",StringUtils.class);
System.out.println(aviatorEvaluatorInstance.execute("StringUtils.isBlank('abc')"));//false
//3.导入实例方法
aviatorEvaluatorInstance.addInstanceFunctions("String",String.class);
System.out.println(aviatorEvaluatorInstance.execute("String.indexOf('abc','b')"));//1
//4.Function Missing调用
// 启用基于反射的方法查找和调用
AviatorEvaluator.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance()); 
// 调用 String#indexOf
System.out.println(AviatorEvaluator.execute("indexOf('hello world', 'w')"));

示例3、使用自定义函数

public class TestAviator {
    public static void main(String[] args) {
            //注册函数
            AviatorEvaluator.addFunction(new AddFunction());
            System.out.println(AviatorEvaluator.execute("add(1, 2)"));           // 3.0
            System.out.println(AviatorEvaluator.execute("add(add(1, 2), 100)")); // 103.0
        }
    }
    class AddFunction extends AbstractFunction {
        @Override
        public AviatorObject call(Map<String, Object> env, 
                                  AviatorObject arg1, AviatorObject arg2) {
            Number left = FunctionUtils.getNumberValue(arg1, env);
            Number right = FunctionUtils.getNumberValue(arg2, env);
            return new AviatorDouble(left.doubleValue() + right.doubleValue());
        }
        public String getName() {
            return "add";
        }
    }

性能执行配置推荐

Aviator具有两种运行模式:
1.执行优先,适合表达式不经常变化,将编译结果缓存并重复运行的场景(默认)
AviatorEvaluator.setOption(Options.OPTIMIZE_LEVEL, AviatorEvaluator.EVAL)
2.编译优先,适合表达式经常变化,不缓存直接运行的场景
AviatorEvaluator.setOption(Options.OPTIMIZE_LEVEL, AviatorEvaluator.COMPILE);
推荐优先使用执行优先模式,使用编译结果缓存模式,复用编译结果,传入不同变量执行

外部变量传入,优先使用编译结果的 Expression#newEnv(…args) 方法创建外部 env,将会启用符号化,降低变量访问开销。

调用 Java 方法,优先使用自定义函数,其次是导入方法,最后是基于 FunctionMissing 的反射模式。

最佳实践

//最佳实践
//编译表达式,并缓存编译对象
Expression abc = aviatorEvaluatorInstance.compile("exp1","a+b+c",true);
//设置环境变量
Map<String, Object> stringObjectMap = abc.newEnv("a", 123, "b", 456, "c", 789);
//执行表达式
Object execute1 = abc.execute(stringObjectMap);
System.out.println(execute1);//1368
//读取缓存
Expression abc2 = aviatorEvaluatorInstance.getCachedExpressionByKey("exp1");
System.out.println(abc == abc2);//true

源码跟踪

我们从最佳实践的流程中跟踪源码的实现方式,主要分为以下的三步:

  1. (编译)Expression abc = aviatorEvaluatorInstance.compile(“exp1”,“a+b+c”,true);
  2. (执行)Object execute1 =abc.execute(stringObjectMap);
  3. (读取缓存)Expression abc2 = aviatorEvaluatorInstance.getCachedExpressionByKey(“exp1”);

编译:

Expression abc = aviatorEvaluatorInstance.compile(“exp1”,“a+b+c”,true);
首先进入compile方法

private Expression compile(final String cacheKey, final String expression,
    final String sourceFile, final boolean cached) {
  /* 忽略检查代码 */
  if (cached) {//如果本次编译需要缓存
    FutureTask<Expression> existedTask = null;
    if (this.expressionLRUCache != null) {//如果开启LRU缓存配置,则缓存入LRU集合
      boolean runTask = false;
      synchronized (this.expressionLRUCache) {//对象加syn锁
        existedTask = this.expressionLRUCache.get(cacheKey);//判断LRU集合是否存在缓存
        if (existedTask == null) {
          //无缓存结果,构建Callable编译任务
          existedTask = newCompileTask(expression, sourceFile, cached);
          runTask = true;
          //将Callable任务存入缓存集合中
          this.expressionLRUCache.put(cacheKey, existedTask);
        }
      }
      if (runTask) {
        existedTask.run();
      }
    } else {//普通缓存模式,存入ConcurrentHashMap集合中
      FutureTask<Expression> task = this.expressionCache.get(cacheKey);
      if (task != null) {
        return getCompiledExpression(expression, task);
      }
      task = newCompileTask(expression, sourceFile, cached);
      existedTask = this.expressionCache.putIfAbsent(cacheKey, task);
      if (existedTask == null) {
        existedTask = task;
        existedTask.run();
      }
    }
    //获取Callable任务的结果,existedTask.get()
    return getCompiledExpression(cacheKey, existedTask);
  } else {//未开启缓存,直接进行编译并返回
    return innerCompile(expression, sourceFile, cached);
  }
}

构建Callable编译任务:newCompileTask实际上也是调用了innerCompile方法,这里我们接着看innerCompile方法

  private FutureTask<Expression> newCompileTask(final String expression, final String sourceFile,final boolean cached) {
    return new FutureTask<>(new Callable<Expression>() {
      @Override
      public Expression call() throws Exception {
        return innerCompile(expression, sourceFile, cached);
      }
    });
  }

innerCompile:

private Expression innerCompile(final String expression, final String sourceFile,
    final boolean cached) {
  //词法分析器,用来对aviator脚本进行词法解析,如将脚本解析为变量、数字、字符串、注释等;
  ExpressionLexer lexer = new ExpressionLexer(this, expression);
  //字节码生成器,用于动态生成自定义的字节码;
  CodeGenerator codeGenerator = newCodeGenerator(sourceFile, cached);
  //表达式解析器,用于将脚本编译为表达式对象(BaseExpression)
  ExpressionParser parser = new ExpressionParser(this, lexer, codeGenerator);
  //通过ASM字节码生成技术生成字节码对象并利用构造函数生成实例对象,这里不进行分析
  Expression exp = parser.parse();
  if (getOptionValue(Options.TRACE_EVAL).bool) {
    ((BaseExpression) exp).setExpression(expression);
  }
  return exp;
}

"a+b+c"表达式生成的字节码对象反编译如下所示

public class SubClassExpression extends ClassExpression {
  private final AviatorJavaType f0;//a
	private final AviatorJavaType f1;//b
	private final AviatorJavaType f2;//c
  public SubClassExpression(final AviatorEvaluatorInstance instance, final List<String> varNames,final SymbolTable symbolTable){
  	super(instance, varNames, symbolTable);
    f0 = new AviatorJavaType("a", symbolTable); 
    f1 = new AviatorJavaType("b", symbolTable); 
    f2 = new AviatorJavaType("c", symbolTable); 
  }
  public final Object execute0(Env env){
  	return f0.add(f1,env).add(f2,env);
  }
}

至此编译动作完成,返回Expression实例对象并加入缓存集合

执行:

Object execute1 =abc.execute(stringObjectMap)
首先看Expression实例对象.execute方法

public Object execute(Map<String, Object> map) {
  if (map == null) {
    map = Collections.emptyMap();
  }
  //环境变量封装
  Env env = genTopEnv(map);
  //环境变量执行器
  EnvProcessor envProcessor = this.instance.getEnvProcessor();
  if (envProcessor != null) {
    envProcessor.beforeExecute(env, this);
  }
  try {
    //执行
    return executeDirectly(env);
  } finally {
    if (envProcessor != null) {
      envProcessor.afterExecute(env, this);
    }
  }
}

接下来看下executeDirectly(Map<String, Object> env)

public Object executeDirectly(final Map<String, Object> env) {
   try {
     //执行ASM生成的字节码实例对象的execute0方法
     Object result = execute0((Env) env);
     if (RuntimeUtils.isTracedEval(env)) {
       RuntimeUtils.printlnTrace(env, "Result : " + result);
     }
     //返回结果,这里是f0.add(f1,env).add(f2,env),123+456+789
     return result;
   } catch (ExpressionRuntimeException e) {
     throw e;
   } catch (Throwable t) {
     throw Reflector.sneakyThrow(t);
   }
 }

读取缓存:

Expression abc2 = aviatorEvaluatorInstance.getCachedExpressionByKey(“exp1”);

 public Expression getCachedExpressionByKey(final String cacheKey) {
   FutureTask<Expression> task = null;
   if (this.expressionLRUCache != null) {//开启了LRU缓存则从LRU缓存中读取
     synchronized (this.expressionLRUCache) {//syn锁,效率不如普通缓存集合
       task = this.expressionLRUCache.get(cacheKey);
     }
   } else {//从普通缓存集合中读取
     task = this.expressionCache.get(cacheKey);
   }
   if (task != null) {
     return getCompiledExpression(cacheKey, task);
   } else {
     return null;
   }
 }

集合类型如下:

//普通集合为ConcurrentHashMap
private final ConcurrentHashMap<String, FutureTask<Expression>> expressionCache =
    new ConcurrentHashMap<String, FutureTask<Expression>>();
//利用LinkedHashMap实现的LRU缓存集合
private LRUMap<String, FutureTask<Expression>> expressionLRUCache;

public class LRUMap<K, V> extends LinkedHashMap<K, V> {
  static final long serialVersionUID = -1L;

  private final int maxCapacity;

public class LRUMap<K, V> extends LinkedHashMap<K, V> {
	public LRUMap(final int maxCapacity) {
	  super(16, 0.75f, true);
	  if (maxCapacity <= 0) {
	    throw new IllegalArgumentException("Invalid maxCapacity: " + maxCapacity);
	  }
	  this.maxCapacity = maxCapacity;
	}
	@Override
	protected boolean removeEldestEntry(final java.util.Map.Entry<K, V> eldest) {
	  return this.size() > this.maxCapacity;
	}
}

应用场景思考

1.公式计算
2.数据处理及转换
3.数据核对
4.工作流逻辑判定
5.鉴权校验
6.规则引擎

实践案例:

1.工作流逻辑判定

业务需求:
“根据不同的角色和动作决定结果”

表达式设计:
“flow(role,action)”

函数说明:
flow() : 获取角色、动作,执行结果

public class RuleEngineDemo {
    public static void main(String[] args) {
        //注册自定义表达式函数
        AviatorEvaluator.addFunction(new FlowFunction());
        //用户指定规则
        String expression = "flow(role,action)";
        Expression compiledExp = AviatorEvaluator.compile(expression);
        //运行时收到数据
        Map<String, Object> fields = new HashMap<String, Object>();
        fields.put("role", "teacher");
        fields.put("action", "talk");
        compiledExp.execute(fields);
        //"The teacher is talking"
        Map<String, Object> fields2 = new HashMap<String, Object>();
        fields.put("role", "student");
        fields.put("action", "study");
        //"The students are studying"
        compiledExp.execute(fields2);
    }
 
    static class FlowFunction extends AbstractFunction {
        @Override
        public void call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
            String role = FunctionUtils.getStringValue(arg1, env);
            String action = FunctionUtils.getStringValue(arg2, env);
            System.out.println("role : " + role + "action : " + action);
			switch(role){
			   case "teacher": {
			       teacher(action);
			       break;
			   }
			   case "student": {
			       student(action);
			       break;
			   }
			   default: {
			       System.out.println("error role");
			       break;
			   }
			}
        }
        public String getName() {
            return "flow";
        }
    }
}

2.数据处理及转换

demo2转载自:http://shihlei.iteye.com/blog/2421576

业务需求:
“1小时,userid,在ip上,触发action 100次报警”

表达式设计:
“redisCount(‘1’,‘hour’,fields(‘userid,ip,action’)) >= 100”

函数说明:
fields() : 获取字段,校验,生成redis key
redisCount():使用 key进行查询,获取redis中存的量且redis +1

public class RuleEngineDemo {
    public static void main(String[] args) {
        //注册自定义表达式函数
        AviatorEvaluator.addFunction(new FieldsFunction());
        AviatorEvaluator.addFunction(new RedisCountFunction());
        //用户指定规则
        String expression = "redisCount('1','hour',fields('userid,ip,action')) >= 100";
        Expression compiledExp = AviatorEvaluator.compile(expression);
        //运行时收到数据
        Map<String, Object> fields = new HashMap<String, Object>();
        fields.put("userid", "9527");
        fields.put("ip", "127.0.0.1");
        fields.put("phone", "18811223344");
        fields.put("action", "click");
        Boolean needAlarm = (Boolean) compiledExp.execute(fields);
        if (needAlarm) {
            System.out.printf("报警");
        }
    }
 
    static class FieldsFunction extends AbstractFunction {
        @Override
        public AviatorObject call(Map<String, Object> env, AviatorObject fieldsStrObj) {
            //获取可变参数
            String fieldStr = fieldsStrObj.stringValue(env);
            String[] fields = fieldStr.split(",");
            StringBuilder redisKey = new StringBuilder();
            System.out.println("FieldsFunction : " + fieldStr);
            for (String f : fields) {
                Object value = env.get(f);
                if (value != null) {
                    redisKey.append(value.toString());
                } else {
                    //TODO 参数合法性校验
                }
                redisKey.append(":");
            }
            return new AviatorString(redisKey.toString());
        }
        public String getName() {
            return "fields";
        }
    }
 
    static class RedisCountFunction extends AbstractFunction {
        @Override
        public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
            String period = FunctionUtils.getStringValue(arg1, env);
            String timeUnit = FunctionUtils.getStringValue(arg2, env);
            String redisKey = FunctionUtils.getStringValue(arg3, env);
            System.out.println("FieldsFunction : " + period + " , " + timeUnit + " , " + redisKey);
            //TODO 读取redis
            int redisCount = redisGetAndIncrease(redisKey);
            return new AviatorLong(redisCount);
        }
 
        private int redisGetAndIncrease(String redisKey) {
            System.out.println("get redis : " + redisKey);
            //这里查询redis获得活动的值;
            return 10000;
        }
        
        public String getName() {
            return "redisCount";
        }
    }
}
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实现一个规则引擎,首先需要定义规则的结构,然后使用aviator进行条件判断和规则执行。aviator是一个高性能的Java表达式解析和计算引擎,可以实现复杂的规则执行逻辑。 在规则引擎中,常见的操作包括条件判断和规则执行。条件判断可以使用aviator表达式解析能力,例如通过判断数值大小、字符串比较、正则表达式匹配等。如果规则需要更复杂的计算逻辑,aviator也支持如加减乘除、逻辑运算和函数调用等操作。 在实现规则引擎时,首先需要定义规则的数据结构,通常使用一个规则对象来表示。规则对象中包含了需要判断的条件和需要执行的操作。可以使用aviator表达式解析功能,解析规则对象中的条件表达式,并根据返回结果来决定是否执行规则对象中定义的操作。 例如,假设我们要实现一个简单的规则引擎来判断一个人的年龄是否满足条件,如果满足则执行相应的操作。我们可以定义一个规则对象,包含条件表达式和操作。条件表达式可以使用aviator表达式解析功能进行判断,例如判断年龄是否大于等于18岁。如果条件满足,则执行相应的操作,例如输出年龄大于等于18岁的人的信息。 总之,使用aviator可以方便地实现规则引擎,通过定义规则对象和使用aviator表达式解析功能,可以实现复杂的条件判断和规则执行逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值