【表达式引擎】简单高效的轻量级Java表达式引擎:Aviator

news/2024/12/23 5:01:59/

简单高效的轻量级表达式引擎:Aviator

前言

Aviator 是一个高性能、、轻量级的表达式引擎,支持表达式动态求值。其设计目标为轻量级和高性能,相比于 GroovyJRuby 的笨重,Aviator 就显得更加的小巧。与其他的轻量级表达式引擎不同,其他的轻量级表达式引擎基本都是通过解释代码的方式来运行,而 Aviator 则是直接将表达式编译成Java字节码,交给JVM来运行。

使用方式

引入依赖

<dependency><groupId>com.googlecode.aviator</groupId><artifactId>aviator</artifactId><version>${version}</version>
</dependency>

简单使用

Aviator 中,我们可以直接定义一个常量表达式来让 Aviator 执行,下面我们通过一个简单的小案例来熟悉一下 Aviator

import com.googlecode.aviator.AviatorEvaluator;
import org.junit.jupiter.api.Test;@Test
void simpleTest(){String expression = "3 + 2 * 6";Object result = AviatorEvaluator.execute(expression);System.out.println(result);
}

image.png

可以看到我们成功运行,且结果就是我们预期的15。

变量表达式

在用过了常量表达式后,我们还可以通过声明变量的形式来实现表达式的运行。

import com.googlecode.aviator.AviatorEvaluator;
import org.junit.jupiter.api.Test;import java.util.HashMap;
import java.util.Map;@Test
void variableTest(){// 1.定义变量Map<String, Object> map = new HashMap<>();map.put("name", "张三");map.put("job", "程序员");// 2.定义表达式String exp = "'你好,我是'+ name + ',我的职业是' + job + ',很高兴认识你'";// 3.使用Aviator执行表达式Object result = AviatorEvaluator.execute(exp, map);// 4.输出结果System.out.println(result);}

image.png

需要注意的是:在书写表达式的时候,格式跟在Java中书写相差不大。由于我们是在字符串中书写的表达式,需要注意 表达式中的字符串也要用单引号包裹起来,否则 Aviator 会将你的字符串判定为一个变量,然后去变量map中去寻找,当找不到的时候就会报错。

自定义函数

在见识过以上两种使用方式后,Aviator 还支持以自定义函数的形式来执行表达式

import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.type.AviatorLong;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.type.AviatorObject;import org.junit.jupiter.api.Test;class CustomFunction extends AbstractFunction{@Overridepublic String getName() {return "customFunc";}@Overridepublic AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {Number num1 = arg1.numberValue(env);Number num2 = arg2.numberValue(env);Long sum = num1.longValue() + num2.longValue();return AviatorLong.valueOf(sum);}@Testvoid customFuncTest(){// 将自定义函数注册到Aviator中AviatorEvaluator.addFunction(new CustomFunction());// 执行Long result = (Long) AviatorEvaluator.execute("customFunc(50,20)");// 输出结果System.out.println(result);}
}

image.png

我们声明完自定义函数后,需要先注册到 Aviator 中之后才能使用,同时我们也可以发现,自定义函数中也是调用的 AviatorEvaluator.execute() 方法,那么就说明调用自定义函数时也是可以做动态变量传值的,如下:

import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.type.AviatorLong;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.type.AviatorObject;import org.junit.jupiter.api.Test;class CustomFunction extends AbstractFunction{@Overridepublic String getName() {return "customFunc";}@Overridepublic AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {Number num1 = arg1.numberValue(env);Number num2 = arg2.numberValue(env);Long sum = num1.longValue() + num2.longValue();return AviatorLong.valueOf(sum);}@Testvoid customFuncTest(){// 声明变量Map<String, Object> map = new HashMap<>();map.put("a", 50);map.put("b", 20);// 将自定义函数注册到Aviator中AviatorEvaluator.addFunction(new CustomFunction());// 执行Long result = (Long) AviatorEvaluator.execute("customFunc(a,b)", map);// 输出结果System.out.println(result);}
}

应用案例

既然学习完了上面三种使用方式,那我们就可以来找个简单的应用案例实操一下了。

假如我们做了一个OA系统,员工每完成一个项目会积累一定的积分,积分可以用来兑换一些公司内自定的奖品,公式我们暂定为:[(总数 * num + 已完成项目数量 * 0.5 -未完成项目数量 * 0.5)/总数] * 10 ,其中 num 的值是根据项目总数变化的,具体公式为(总数去除个位数) / 1000 + 0.9,并且这个公式可能在之后的场景里会改变,有了场景我们就来具体实现一下。

建表

首先我们要先建个表来存放我们的公式。

/*Navicat Premium Data TransferSource Server         : localhostSource Server Type    : MySQLSource Server Version : 80032 (8.0.32)Source Host           : localhost:3306Source Schema         : studyTarget Server Type    : MySQLTarget Server Version : 80032 (8.0.32)File Encoding         : 65001Date: 22/07/2023 14:31:01
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for aviator_expression
-- ----------------------------
DROP TABLE IF EXISTS `aviator_expression`;
CREATE TABLE `aviator_expression`  (`var_id` int NOT NULL AUTO_INCREMENT COMMENT '表达式id',`var_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '表达式名称',`expression` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '表达式',`create_user_id` int NULL DEFAULT NULL COMMENT '创建用户id',`create_user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建用户名称',`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_user_id` int NULL DEFAULT NULL COMMENT '最后修改用户id',`update_user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '最后修改用户名称',`update_time` datetime NULL DEFAULT NULL COMMENT '最后修改时间',PRIMARY KEY (`var_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of aviator_expression
-- ----------------------------
INSERT INTO `aviator_expression` VALUES (1, '计算个人积分', '(total * double(num) + completedNum * 0.5 - unFinishedNum * 0.5) * 10', 0, '超级管理员', '2023-07-22 11:00:48', NULL, NULL, NULL);
INSERT INTO `aviator_expression` VALUES (2, '计算个人积分子项', 'totalInt / 1000 + 0.9', 0, '超级管理员', '2023-07-22 14:30:48', NULL, NULL, NULL);-- ----------------------------
-- Table structure for employee_project
-- ----------------------------
DROP TABLE IF EXISTS `employee_project`;
CREATE TABLE `employee_project`  (`emp_id` int NOT NULL AUTO_INCREMENT COMMENT '员工id',`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '员工名称',`total` int NULL DEFAULT NULL COMMENT '项目总数',`completed_num` int NULL DEFAULT NULL COMMENT '已完成项目数量',`unFinished_num` int NULL DEFAULT NULL COMMENT '未完成项目数量',PRIMARY KEY (`emp_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of employee_project
-- ----------------------------
INSERT INTO `employee_project` VALUES (1, '张三', 9, 7, 2);
INSERT INTO `employee_project` VALUES (2, '李四', 10, 9, 1);
INSERT INTO `employee_project` VALUES (3, '王五', 23, 20, 3);SET FOREIGN_KEY_CHECKS = 1;

实现

Controller

import com.aviator.service.AviatorExpressionService;
import com.aviator.vo.PerformanceVo;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @author Bummon* @description* @date 2023-07-22 14:32*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/performance")
public class PerformanceController {private final AviatorExpressionService expressionService;@GetMapping("/calculate")public List<PerformanceVo> calculatePerformance() {return expressionService.calculatePerformance();}}

Service

import com.aviator.entity.AviatorExpression;
import com.aviator.vo.PerformanceVo;
import com.baomidou.mybatisplus.extension.service.IService;import java.util.List;/*** @author Bummon* @description 针对表【aviator_expression】的数据库操作Service* @createDate 2023-07-22 14:32:24*/
public interface AviatorExpressionService extends IService<AviatorExpression> {/*** @return {@link List< PerformanceVo>}* @date 2023-07-22 14:36* @author Bummon* @description 计算绩效*/List<PerformanceVo> calculatePerformance();}

ServiceImpl

import cn.hutool.core.bean.BeanUtil;
import com.aviator.entity.EmployeeProject;
import com.aviator.service.EmployeeProjectService;
import com.aviator.vo.PerformanceVo;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.googlecode.aviator.AviatorEvaluator;
import lombok.RequiredArgsConstructor;
import com.aviator.entity.AviatorExpression;
import com.aviator.service.AviatorExpressionService;
import com.aviator.mapper.AviatorExpressionMapper;
import org.springframework.stereotype.Service;import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author Bummon* @description 针对表【aviator_expression】的数据库操作Service实现* @createDate 2023-07-22 14:32:24*/
@Service
@RequiredArgsConstructor
public class AviatorExpressionServiceImpl extends ServiceImpl<AviatorExpressionMapper, AviatorExpression>implements AviatorExpressionService {private final EmployeeProjectService employeeProjectService;@Overridepublic List<PerformanceVo> calculatePerformance() {//员工项目完成项目情况列表List<EmployeeProject> employeeProjectList = employeeProjectService.list();List<PerformanceVo> performanceList = BeanUtil.copyToList(employeeProjectList, PerformanceVo.class);//计算绩效公式AviatorExpression performanceExp = this.getOne(Wrappers.lambdaQuery(AviatorExpression.class).eq(AviatorExpression::getVarName, "团队积分计算"));//绩效子项计算AviatorExpression performanceChildExp = this.getOne(Wrappers.lambdaQuery(AviatorExpression.class).eq(AviatorExpression::getVarName, "计算团队积分子项"));performanceList.forEach(any -> {//利用int向下取整的特性去除整数最后一位int totalInt = any.getTotal() / 10;Map<String, Object> map = new HashMap<>();map.put("totalInt", totalInt);//计算绩效子项Double num = (Double) AviatorEvaluator.execute(performanceChildExp.getExpression(), map);//计算绩效,并保留一位小数Map<String, Object> paramMap = BeanUtil.beanToMap(any);paramMap.put("num", num);Double doublePerformance = (Double) AviatorEvaluator.execute(performanceExp.getExpression(), paramMap);DecimalFormat df = new DecimalFormat("#.#");String performance = df.format(doublePerformance);any.setPerformance(performance);});return performanceList;}
}

image.png

由于num在传入的时候数据类型为String 类型,但是 Aviator 对数据类型要求比较严格,所以我们要在表达式里面将num 转为 double 类型。

总结

我们一共学习了 Aviator 的三种用法:简单表达式变量表达式自定义函数
同时也通过一个简单的小案例供大家学习参考,大家也可以根据实际应用场景的不同来自行更改实现方式。

至此教程就结束啦,感谢大家的关注。


http://www.ppmy.cn/news/973751.html

相关文章

ElasticSearch学习--操作

目录 索引库操作 mapping映射 总结 创建索引库 查询删除修改索引库 总结 文档操作 添加 查询&#xff0c;删除 修改文档 总结 RestClient操作索引库 初始化JavaRestClient 创建索引库​编辑 删除索引库&#xff0c;判断索引库是否存在 总结 操作文档 新增文档 查询文…

element-ui动态编辑标签

点击叉叉&#xff0c;标签消失&#xff0c;点击New Tag&#xff0c;显示输入框&#xff0c;输入完成后生成标签&#xff0c;并且出现New Tag标签。 代码&#xff1a; <el-tag:key"tag"v-for"tag in dynamicTags"closable:disable-transitions"fa…

亲爱的小朋友,你好,今天我们聊一聊什么是ChatGPT?

亲爱的小朋友&#xff0c;你好&#xff01; 今天我们要聊一聊的是我们的好朋友——ChatGPT&#xff0c;最近ChatGPT很火爆&#xff0c;你们可能已经在很多地方看到关于它的文章和视频了。 你可能会问&#xff0c;这个ChatGPT到底是什么东西呢&#xff1f;好像很厉害的样子&am…

【社区图书馆】读后感《你好,ChatGPT》

&#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&#xff08;网站、工具、素材、源码、游戏等&#xff09; 有什么需要欢迎底部卡片私我&#xff0c;获取更多支持&#xff0c;交流让学习不再孤单。 前两天收到了CSDN发的一本书&…

OpenCv之视频人脸识别

一、人脸检测 案例代码如下: import cv2 import numpy as npvideo cv2.VideoCapture(1.mp4) face_detector cv2.CascadeClassifier(haarcascade_frontalface_alt.xml) while True:retval,image video.read() # retval boolean类型表名是否获得了图片if not retval:pr…

arcgis-利用等高线数据生成dem栅格

1、打开cass&#xff0c;展高程点&#xff0c;绘制三角网&#xff0c;绘制等高线&#xff0c;删除三角网和高程点。如下&#xff1a; 2、得到的等高线图&#xff0c;如下&#xff1a; 3、保存文件为dwg格式&#xff0c;随后打开arcmap软件&#xff0c;打开dwg的线层数据&#x…

airtest-selenium 脚本爬取百度热搜标题

目录 1. 前言 2. 爬取标题的脚本 3. 命令行运行 Web 自动化脚本 1&#xff09;python 环境准备 2&#xff09;chrome 与 chromedriver 版本对应 3&#xff09;命令行运行 1. 前言 airtest-selenium是一个基于Python的UI自动化测试框架&#xff0c;它结合了airtest和sele…

【数据结构】--八大排序算法【完整版】

匠心制作&#xff0c;后续有问题会加以修改的 &#xff0c;全文均是自己写的&#xff0c;几张图有参考网络 ———————————————— 目录 一、直接插入排序 二、希尔排序(直接插入排序的改良版) 三、选择排序&#xff08;直接选择排序&#xff09; 四、堆排序 …