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

简单高效的轻量级表达式引擎: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{
  @Override
    public String getName() {
      return "customFunc";
    }

  @Override
  public 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);
  }

  @Test
  void 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{
  @Override
    public String getName() {
      return "customFunc";
    }

  @Override
  public 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);
  }

  @Test
  void 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 Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80032 (8.0.32)
 Source Host           : localhost:3306
 Source Schema         : study

 Target Server Type    : MySQL
 Target Server Version : 80032 (8.0.32)
 File Encoding         : 65001

 Date: 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;

    @Override
    public 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.mfbz.cn/a/43630.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python实战项目——物流行业数据分析(二)

今天我们对物流行业数据进行简单分析&#xff0c;数据来源&#xff1a;某企业销售的6种商品所对应的送货及用户反馈数据 解决问题&#xff1a; 1、配送服务是否存在问题 2、是否存在尚有潜力的销售区域 3、商品是否存在质量问题 分析过程&#xff1a; 依旧先进行数据处理 一…

【MATLAB第58期】基于MATLAB的PCA-Kmeans、PCA-LVQ与BP神经网络分类预测模型对比

【MATLAB第58期】基于MATLAB的PCA-Kmeans、PCA-LVQ与BP神经网络分类预测模型对比 一、数据介绍 基于UCI葡萄酒数据集进行葡萄酒分类及产地预测 共包含178组样本数据&#xff0c;来源于三个葡萄酒产地&#xff0c;每组数据包含产地标签及13种化学元素含量&#xff0c;即已知类…

C# List 详解四

目录 18.FindLast(Predicate) 19.FindLastIndex(Int32, Int32, Predicate) 20.FindLastIndex(Int32, Predicate) 21.FindLastIndex(Predicate) 22.ForEach(Action) 23.GetEnumerator() 24.GetHashCode() 25.GetRange(Int32, Int32) C#…

【搜索引擎Solr】配置 Solr 以获得最佳性能

Apache Solr 是广泛使用的搜索引擎。有几个著名的平台使用 Solr&#xff1b;Netflix 和 Instagram 是其中的一些名称。我们在 tajawal 的应用程序中一直使用 Solr 和 ElasticSearch。在这篇文章中&#xff0c;我将为您提供一些关于如何编写优化的 Schema 文件的技巧。我们不会讨…

Prompt 技巧指南-让 ChatGPT 回答更准确

随着 ChatGPT 等大型语言模型 (LLM)的兴起&#xff0c;人们慢慢发现&#xff0c;怎么样向 LLM 提问、以什么技巧提问&#xff0c;是获得更加准确的回答的关键&#xff0c;也由此产生了提示工程这个全新的领域。 提示工程(prompt engineering)是一门相对较新的领域&#xff0c;用…

多目标灰狼算法(MOGWO)的Matlab代码详细注释及难点解释(佳点集改进初始种群的MOGWO)

目录 一、外部种群Archive机制 二、领导者选择机制 三、多目标灰狼算法运行步骤 四、MOGWO的Matlab部分代码详细注释 五、MOGWO算法难点解释 5.1 网格与膨胀因子 5.2 轮盘赌方法选择每个超立方体概率 为了将灰狼算法应用于多目标优化问题,在灰狼算法中引入外部种群Archi…

【人工智能】大模型平台新贵——文心千帆

个人主页&#xff1a;【&#x1f60a;个人主页】 &#x1f31e;热爱编程&#xff0c;热爱生活&#x1f31e; 文章目录 前言大模型平台文心千帆发布会推理能力模型微调 作用 前言 在不久的之前我们曾讨论过在ChatGPT爆火的大环境下&#xff0c;百度推出的“中国版ChatGPT”—文…

【C++】STL使用仿函数控制优先级队列priority_queue

文章目录 前言一、priority_queue的底层实现二、使用仿函数控制priority_queue的底层总结 前言 本文章讲解CSTL的容器适配器&#xff1a;priority_queue的实现&#xff0c;并实现仿函数控制priority_queue底层。 一、priority_queue的底层实现 priority_queue叫做优先级队列&…

C进阶:文件操作

C语言文件操作 什么是文件 磁盘上的数据是文件。 但是在程序设计中&#xff0c;我们一般谈的文件有两种&#xff1a;程序文件&#xff08;例如.c,.h这一类编译&#xff0c;链接过程中的文件&#xff09;&#xff0c;数据文件。 程序文件 包括源程序文件&#xff08;后缀为.c&…

【PostgreSQL内核学习(十)—— 查询执行(可优化语句执行)】

可优化语句执行 概述物理代数与处理模型物理操作符的数据结构执行器的运行 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c;力求遵循合理使用原则&#xff0c;并在适用的情况下注明引用来源。 本文主要…

macOS系统下编译linux-adk源码

1.下载 linux-adk源码 https://github.com/gibsson/linux-adk.git 2.安装libusb库 brew install libusb 3.修改Makefile CFLAGS += -Isrc -I/usr/local/Cellar/libusb/1.0.26/include/libusb-1.0 4.编译 make ./linux-adk -h 查看用法 查看系统已连接USB设备 system_p…

C#使用Linq和Loop计算集合的平均值、方差【标准差】

方差【标准差】 标准差公式是一种数学公式。标准差也被称为标准偏差&#xff0c;或者实验标准差&#xff0c;公式如下所示&#xff1a; 样本标准差方差的算术平方根ssqrt(((x1-x)^2 (x2-x)^2 ......(xn-x)^2)/n) 总体标准差σsqrt(((x1-x)^2 (x2-x)^2 ......(xn-x)^2)/n ) …

win11我们无法创建新的分区也找不到现有的分区

U盘重装系统的时候 提示&#xff1a;win11我们无法创建新的分区也找不到现有的分区 ShiftF10 &#xff0c;调出 命令提示符&#xff1b; diskpart list disk select disk 盘编号 clean convert gpt 参考&#xff1a;怎么解决我们无法创建新的分区也找不到现有的分区问题&#x…

OpenCV实现照片换底色处理

目录 1.导言 2.引言 3.代码分析 4.优化改进 5.总结 1.导言 在图像处理领域&#xff0c;OpenCV是一款强大而广泛应用的开源库&#xff0c;能够提供丰富的图像处理和计算机视觉功能。本篇博客将介绍如何利用Qt 编辑器调用OpenCV库对照片进行换底色处理&#xff0c;实现更加…

HTTP 缓存机制 强制缓存/协商缓存

为什么被缓存&#xff0c;如何命中缓存以及缓存什么时候生效的&#xff0c;我们却很少在实际开发中去了解。借助动画形式来从根上理解 HTTP 缓存机制及原理。 HTTP 缓存&#xff0c;对于前端的性能优化方面来讲&#xff0c;是非常关键的&#xff0c;从缓存中读取数据和直接向服…

REST和RPC的区别

1 REST REST 不是一种协议&#xff0c;它是一种架构。大部分REST的实现中使用了RPC的机制&#xff0c;大致由三部分组成&#xff1a; method&#xff1a;动词&#xff08;GET、POST、PUT、DELETE之类的&#xff09;Host&#xff1a;URI&#xff08;统一资源标识&#xff09;&…

前端Vue uni-app App/小程序/H5 通用tree树形结构图

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。 通过组件化开发&#xff0c;可以有效实现…

Apple M1 Pro macOS 切换中文输入法卡住

(macOS 在切换中文输入法时出现卡住的情况 1&#xff0c;切换为中文输入法后再次卡住2&#xff0c;杀死 简体中文输入方式的进程参考 将光标移到菜单栏的输入法切换为英文输入法 多次切换为英文输入法&#xff0c;可以切换为英文输入法 切换为英文输入法后电脑不卡顿了&#xf…

云原生基础设施实践:NebulaGraph 的 KubeBlocks 集成故事

像是 NebulaGraph 这类基础设施上云&#xff0c;通用的方法一般是将线下物理机替换成云端的虚拟资源&#xff0c;依托各大云服务厂商实现“服务上云”。但还有一种选择&#xff0c;就是依托云数据基础设施&#xff0c;将数据库产品变成为云生态的一环&#xff0c;不只是提供自身…

《面试1v1》如何提高远程用户的吞吐量

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…