java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > springboot 表达式计算引擎 Aviator

springboot 整合表达式计算引擎 Aviator 使用示例详解

作者:小码农叔叔

本文详细介绍了Google Aviator 这款高性能、轻量级的 Java 表达式求值引擎,并通过详细的代码操作演示了相关API的使用以及如何在springboot项目中进行集成,感兴趣的朋友一起看看吧

一、前言

在项目开发中,通常会遇到诸如各类表达式计算的需求,比如根据页面输入的某种条件参数,服务端计算得到一种结果,在输入另一种条件参数,服务端计算得到另一种结果,对于简单的条件来说,服务端可以通过简单的条件判断并解析参数,组装计算表达式即可完成,但设想以后类似的场景越来越多,也越来越复杂时,这必然会造成代码的臃肿和维护成本的不断提高,于是,表达式计算求值框架就出现了。

二、表达式计算框架概述

在正式介绍表达式计算框架之前,需要搞懂与规则引擎的异同点,否则在实际场景中进行选择时很容易混淆,虽然两者之间存在一些异同点。

2.1 规则引擎

2.1.1 什么是规则引擎

规则引擎(Rule Engine)是一种软件组件或系统,它允许用户通过定义规则来控制业务逻辑或应用程序的行为。这些规则通常是基于业务策略或决策逻辑的声明性语句,用于指导软件如何响应不同的情况或事件。

规则引擎通过自动化决策过程,帮助开发者将复杂的业务逻辑从应用程序代码中分离出来,从而提高了应用程序的灵活性、可维护性和可扩展性。

2.1.2 规则引擎用途

规则引擎可被定义为一种软件架构或技术,它提供一种机制来定义、部署、执行和管理业务规则。通过将这些规则从应用程序代码中分离出来,并使用专门的规则引擎来管理和执行它们,开发者可以更容易地实现和维护复杂的业务逻辑,同时保持应用程序的灵活性和可扩展性。它具备如下用途:

2.1.3 规则引擎使用场景

规则引擎作为一种能够自动执行预设逻辑的软件工具,使用场景非常广泛,涵盖多个行业和领域。以下列举了规则引擎的一些主要使用场景:

2.2 表达式计算框架

2.2.1 表达式计算框架定义

表达式计算框架(Expression Evaluation Framework)是一个用于解析、计算和处理表达式的软件架构或系统。它允许用户输入包含变量、运算符和可能的其他元素(如函数、括号等)的表达式,并自动计算其结果。

表达式计算框架在多个领域都有广泛应用,包括但不限于编程语言、数据库查询、业务规则引擎、科学计算等。

2.2.2 表达式计算框架特点

表达式计算框架具备如下特点:

2.2.3 表达式计算框架应用场景

表达式计算框架的应用场景非常广泛,涵盖了从基础编程到复杂业务逻辑的多个方面。以下是几个主要的应用场景:

2.3 表达式计算框架与规则引擎异同点

规则引擎和表达式计算框架在功能上有一些重叠,但在用途和设计目的上有明显的区别。下面对其异同点做一些总结:

2.3.1 相同点

2.3.2 不同点

两者在进行技术选择上更多出于各自的应用场景的差异

三、表达式计算框架 Google Aviator介绍

3.1 Google Aviator概述

3.1.1 Aviator是什么

Google Aviator 是一个高性能、轻量级的 Java 表达式求值引擎,它的设计目的是为了提供快速的表达式计算能力,Aviator 主要用于动态计算表达式,尤其是在需要频繁计算的场景中表现出色。

3.1.2 Aviator特点

Google Aviator具备如下特点:

3.2 Google Aviator 优势和应用场景

Google Aviator 作为一种高性能、轻量级的 Java 表达式求值引擎,非常适合在需要动态计算表达式的场景中使用,下面总结了一些 Aviator 的典型使用场景:

Aviator 的主要优势在于它能够提供快速、灵活的表达式计算能力,因此在任何需要频繁计算表达式的场景中都可以发挥重要作用。

四、Google Aviator使用

接下来通过代码的操作演示Aviator如何使用,前置准备,以springboot项目为例,在你的工程中引入如下依赖

        <dependency>
            <groupId>com.googlecode.aviator</groupId>
            <artifactId>aviator</artifactId>
            <version>5.3.3</version>
        </dependency>

4.1 Aviator常用运算符操作

4.1.1 入门示例

在工程的某个包目录下创建一个hello.av的文件,在该文件中编写计算规则的表达式,然后再在代码中进行调用,这也是使用Aviator的一个基本的思路,如下

在hello.av中添加如下内容

println("hello aviator");

HelloWord代码如下:

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
 
public class HelloWord {
 
    public static void main(String[] args) throws Exception{
        String helloPath = "E:\\code-self\\117\\sa-token\\src\\main\\java\\com\\congge\\aviator\\hello.av";
        Expression expression = AviatorEvaluator.getInstance().compileScript(helloPath);
        expression.execute();
    }
 
}

运行之后效果如下,即可将hello.av中的内容进行输出

如果表达式比较简单,也可以直接在代码中进行定义,如下:

public static void main(String[] args) throws Exception{
        String helloPath = "E:\\code-self\\117\\sa-token\\src\\main\\java\\com\\congge\\aviator\\hello.av";
        Expression expression = AviatorEvaluator.getInstance().compileScript(helloPath);
        expression.execute();
        String expr2 = "println(\"hello aviator\");";
        Expression expression2 = AviatorEvaluator.getInstance().compile(expr2);
        expression2.execute();
    }

4.1.2 算术运算符

作为一门表达式计算框架,aviator支持常用的 加减乘除的计算,参考下面的示例

public static void main(String[] args) {
        compute("println(1+3)");
        compute("p(1+3)");
        compute("p(5-3)");
        compute("p(5*3)");
        compute("p(9/3)");
        compute("p((3 + 2) * 5)");
    }
    public static void compute(String expr){
        Expression compile = AviatorEvaluator.compile(expr);
        compile.execute();
    }

运行得到计算结果

4.1.3 比较运算符

aviator提供了比较运算符用于数据比较的场景,参考下面的示例

    public static void main(String[] args) {
        //比较运算符
        compute("p(3==3)");
        compute("p(3!=2)");
        compute("p(3<2)");
    }
    public static void compute(String expr){
        Expression compile = AviatorEvaluator.compile(expr);
        compile.execute();
    }

4.1.4 逻辑操作符

    public static void main(String[] args) {
        //逻辑操作运算符
        compute("p( 3!=2 && 3<2)");
        compute("p( !(3>2))");
    }
    public static void compute(String expr){
        Expression compile = AviatorEvaluator.compile(expr);
        compile.execute();
    }

4.1.5 运算符操作补充

如果代码中涉及的运算符计算比较多,也可以写在.av文件中,然后程序中调用,格式符合aviator的规范即可,如下示例

let a = 10;
let b = 11;
if a > b {
  p("#{a} > #{b}");
}elsif a < b {
  p("#{a} < #{b}");
}else {
    p("#{a} = #{b}");
}

然后再在程序中进行调用

public static void main(String[] args) throws Exception{
        Expression expression = AviatorEvaluator.getInstance().compileScript("E:\\code-self\\117\\sa-token\\src\\main\\java\\com\\congge\\aviator\\condition.av");
        expression.execute();
    }

4.2 Aviator内置函数使用

像很多技术框架一样都会提供一些内置的函数,可供开发者开箱即用,Aviator也提供了一些常用的内置函数,下面通过代码示例来感受下。

4.2.1 随机数函数

rand()函数可以生成一个随机数

compute("p(rand(10))");

4.2.2 数值比较函数

函数格式:cmp(number1,number2)

compute("p(cmp(5,9))");
compute("p(cmp(10,9))");
compute("p(cmp(9,9))");

4.2.3 强制类型转换

函数格式:cmp(number1,number2)

//数据类型强制转换  字符串转为 long
compute("p(long('1'))");

4.2.4 数值类型判断

判断某个值是否某个类型,函数:is_a()

//is_a , 判断某个值是否某个类型的实例
compute("p(is_a('hello',String))");
compute("p(is_a(5,Long))");
compute("p(is_a(1.3,Double))");

4.2.5 常用的字符串函数

字符串函数在开发中是一种高频使用的函数,下面列举几种

//字符串是否包含
compute("p(string.contains('hello aviator','aviator'))");
//字符串长度
compute("p(string.length('hello aviator'))");
//字符串截取
compute("p(string.substring('hello aviator',4,9))");
//字符串位置
compute("p(string.indexOf('hello aviator','hello'))");

4.2.6 常用数学函数

//最大值,从一组数值中找到最大的
compute("p(max(1,12,8,1.2,23,18))");
//绝对值
compute("p(math.abs(-1))");
//四舍五入
compute("p(math.round(3.3))");

4.2.7 集合操作函数

数组:

//创建指定类型的数组
compute("p(seq.array(long,1,2,3,4,5))");

arraylist:

//创建一个arraylist
compute("p(seq.list(1,2,3,4,5))");

arraylist使用脚本操作:

let a = seq.list(1,2,3,4,5);
seq.add(a,15);
p(a);

set操作

compute("p(seq.set(1,2,3,5))");

map操作

compute("p(seq.map('k1','v1','k2','v2'))");

4.3 Aviator自定义函数使用

在某些相对复杂的场景下,内置函数搞不定的时候,自定义函数就派上用场了,在实际使用自定义函数时,主要有两种方式,在.av文件中自定义函数,或者在class类中通过编码方式定义函数;

4.3.1 在外部文件中定义函数

如下,在func.av文件中定义如下函数

fn add(a,b){
    return a+b;
}
res = add(11,12);
println("add res = #{res}")

再在程序中调用该函数

Expression addExpr = AviatorEvaluator.getInstance().compileScript("E:\\code-self\\117\\sa-token\\src\\main\\java\\com\\congge\\aviator\\func.av");
addExpr.execute();

执行结果

4.3.2 继承AbstractFunction

也可以通过继承AbstractFunction抽象类,并重写里面的相关方法完成自定义函数,如下两个自定义方法中,继承了AbstractFunction类之后,里面有很多待重写的方法可供使用,选择一个符合自己需求的方法即可:

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorDouble;
import com.googlecode.aviator.runtime.type.AviatorLong;
import com.googlecode.aviator.runtime.type.AviatorObject;
import java.util.Collection;
import java.util.Map;
public class OwnFunction {
    public static void main(String[] args) {
        AviatorEvaluator.addFunction(new AddFunc());
        Object executeRes = AviatorEvaluator.execute("addMethod(11,12)");
        System.out.println(executeRes);
        AviatorEvaluator.addFunction(new SumFunc());
        Object executeResSum = AviatorEvaluator.execute("sumMethod(seq.list(1,2,3,4,5))");
        System.out.println(executeResSum);
    }
}
class AddFunc extends AbstractFunction {
    @Override
    public String getName() {
        return "addMethod";
    }
    @Override
    public AviatorObject call(Map<String, Object> env, AviatorObject arg1,AviatorObject arg2) {
        Number num1 = FunctionUtils.getNumberValue(arg1, env);
        Number num2 = FunctionUtils.getNumberValue(arg2, env);
        return new AviatorDouble(num1.doubleValue() + num2.doubleValue());
    }
}
class SumFunc extends AbstractFunction {
    @Override
    public String getName() {
        return "sumMethod";
    }
    @Override
    public AviatorObject call(Map<String, Object> env, AviatorObject arg1) {
       Collection<Object> collection = (Collection<Object>)arg1.getValue(env);
       long sum = 0;
       for(Object item :collection ){
           sum += (long)item;
       }
       return AviatorLong.valueOf(sum);
    }
}

方法1:实现两个数相加

方法2:实现一个列表中的元素累加求和

五、springboot项目集成Aviator

5.1 使用场景

在正式开始之前,如果在你的springboot项目集成并使用Aviator,可以按照需求先提取几种常用的场景。

5.1.1 自定义规则表达式

核心思路和步骤分为下面几步:

这类场景在一些涉及到交换的场景,或者一些数据中台的场景中多有运用

5.1.2 提前定义计算表达式

这种方式即提前根据需求编制一些计算表达式,并集中存储到数据库或配置文件中,在需要使用的时候,通过传入计算要用到的参数,调用提前配置的表达式进行计算,要求业务一般不会发生太大的变动下可以考虑使用。

5.2 整合使用过程

下面介绍完整的整合步骤

5.2.1 自定义Aviator工具类

为了便于在需要计算表达式值得逻辑中使用,建议自定义一个工具类方便调用,工具类中还可以自定义的方法等

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AviatorUtils {
    private String expression;
    /**
     * 根据入参和表达式计算结果
     * @param env
     * @return
     */
    public Object execute(Map<String, Object> env) {
        //添加自定义函数
        //AviatorEvaluator.addFunction(new AddFunc());
        Object execute = AviatorEvaluator.execute(expression, env);
        return execute;
    }
}

5.2.2 添加测试接口

为方便测试效果,添加一个测试接口

import com.congge.config.AviatorUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class AviatorController {
    //localhost:8081/compute?expression=(x+y) * 3
    @GetMapping("/compute")
    public Object compute(@RequestParam String expression){
        Map<String, Object> env = new HashMap<>();
        env.put("x", 10);
        env.put("y", 20);
        AviatorUtils aviatorUtils = new AviatorUtils(expression);
        Object executeRes = aviatorUtils.execute(env);
        return executeRes;
    }
}

5.2.3 效果测试

调用上述的接口:http://localhost:8081/compute?expression=x*y

六、写在文末

本文通过较大的篇幅详细介绍了Google Aviator 这款高性能、轻量级的 Java 表达式求值引擎,并通过详细的代码操作演示了相关API的使用以及如何在springboot项目中进行集成,希望对看到的同学有用,本篇到此结束,感谢观看。

到此这篇关于springboot 整合表达式计算引擎 Aviator 使用详解的文章就介绍到这了,更多相关springboot 表达式计算引擎 Aviator内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文