1、原理和特点
Aviator 的基本过程是将表达式直接翻译成对应的 java 字节码执行,整个过程最多扫两趟(开启执行优先模式,如果是编译优先模式下就一趟),这样就保证了它的性能超越绝大部分解释性的表达式引擎,测试也证明如此;其次,除了依赖 commons-
beanutils
这个库之外(用于做反射)不依赖任何第三方库,因此整体非常轻量级,整个 jar 包大小哪怕发展到现在 5.0 这个大版本,也才 430K。同时, Aviator 内置的函数库非常“节制”,除了必须的字符串处理、数学函数和集合处理之外,类似文件 IO、网络等等你都是没法使用的,这样能保证运行期的安全,如果你需要这些高阶能力,可以通过开放的自定义函数来接入。因此总结它的特点是:
- 高性能
- 轻量级
- 一些比较有特色的特点:
-
- 支持运算符重载
- 原生支持大整数和 BigDecimal 类型及运算,并且通过运算符重载和一般数字类型保持一致的运算方式。
-
- 原生支持正则表达式类型及匹配运算符
=~
- 类 clojure 的 seq 库及 lambda 支持,可以灵活地处理各种集合
- 原生支持正则表达式类型及匹配运算符
- 开放能力:包括自定义函数接入以及各种定制选项
为什么还要有 aviator ?
这是有人向我提出来的问题,既然 groovy/kotlin/jruby 都发展的这么好,还有没有必要发展 AviatorScript?我直接用他们不也是挺好的。
我的答案是没错,优先使用社区广泛使用的语言,有一个比较好的社区支持,这都是很好、很正确的考量。那么为什么还想要发展和去使用 AviatorScript? 我能想到的理由如下:
- 我可以,并且我将继续维护它
- 你不想使用一个全功能的、相对重量级的语言,你只是做一些布尔表达式判定、数据集合处理等等,你不想引入一堆依赖,并且期待有一定的性能保证。AviatorScript 提供了大量的定制选项,甚至各种语法特性都是可以开关的。
- 你的表达式或者 script 是用户输入的,你无法保证他们的安全性,你希望控制用户能使用的 API,提供一个相对安全的运行沙箱
- 你原来的项目在使用 aviator,有一堆遗留的表达式要维护,原来没有很好的条件语句、循环语句等能力支持,让你的使用受限了。
- 你就是喜欢 AviatorScript 的语法
- 你就是想用它
2、示例
添加AviatorScript依赖:在Spring Boot项目的pom.xml
中添加AviatorScript的依赖。
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.3.0</version>
</dependency>
创建脚本文件:创建一个AviatorScript脚本文件,比如命名为calculation.av
,并在其中写入你的计算公式。
# 定义一个计算函数
fn calculate_expression(a, b, c) {
return a + b * c;
}
# 调用函数并返回结果
result = calculate_expression(a, b, c);
在Java中使用脚本文件:在Spring Boot中,你可以使用AviatorEvaluator
来编译和执行脚本文件,并动态传入变量。
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorRuntimeJavaType;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class AviatorScriptService {
// 编译脚本
public Expression compileScript(String scriptPath) {
try {
return AviatorEvaluator.compileScript(new FileReader(scriptPath), true);
} catch (IOException e) {
throw new RuntimeException("Error compiling script", e);
}
}
// 执行脚本并传入变量
public Object executeScript(Expression expression, Map<String, Object> variables) {
return expression.execute(variables);
}
// 调用脚本并返回结果
public Object callScript(String scriptPath, Map<String, Object> variables) {
Expression expression = compileScript(scriptPath);
return executeScript(expression, variables);
}
}
在Controller中调用服务:创建一个Controller来处理请求并调用服务
import org.springframework.beans.factory.annotation.Autowired;
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 AviatorScriptController {
@Autowired
private AviatorScriptService aviatorScriptService;
@GetMapping("/calculate")
public Object calculate(@RequestParam Double a, @RequestParam Double b, @RequestParam Double c) {
Map<String, Object> variables = new HashMap<>();
variables.put("a", a);
variables.put("b", b);
variables.put("c", c);
return aviatorScriptService.callScript("classpath:calculation.av", variables);
}
}