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

这篇具有很好参考价值的文章主要介绍了【表达式引擎】简单高效的轻量级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);
}

java表达式解析引擎,思路分享,学习笔记,java,数据库,spring boot

可以看到我们成功运行,且结果就是我们预期的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);
  
}

java表达式解析引擎,思路分享,学习笔记,java,数据库,spring boot

需要注意的是:在书写表达式的时候,格式跟在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);
  }
}

java表达式解析引擎,思路分享,学习笔记,java,数据库,spring boot

我们声明完自定义函数后,需要先注册到 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;
    }
}

java表达式解析引擎,思路分享,学习笔记,java,数据库,spring boot

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

总结

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

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


推荐

关注博客和公众号获取最新文章

Bummon’s Blog | Bummon’s Home | 公众号文章来源地址https://www.toymoban.com/news/detail-706021.html

到了这里,关于【表达式引擎】简单高效的轻量级Java表达式引擎:Aviator的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring Boot】Thymeleaf模板引擎 — 表达式的语法

    模板的主要作用是将后台返回的数据渲染到HTML中。那么Thymeleaf是如何解析后台数据的呢?接下来从变量、方法、条件判断、循环、运算(逻辑运算、布尔运算、比较运算、条件运算)方面学习Thymeleaf表达式支持的语法。 (1)文本赋值 赋值就是通过${}标签将后台返回的数据替

    2024年02月14日
    浏览(39)
  • Python中的三元运算符:简洁高效的条件表达式

    什么是三元运算符 三元运算符是一种简洁高效的条件表达式,用于根据条件的真假来返回不同的值。它是一种特殊的运算符,由三个操作数组成,形式为 [结果为真的值] if [条件] else [结果为假的值] 。 Python中的三元运算符语法 Python中的三元运算符的语法如下: 三元运算符的

    2024年01月19日
    浏览(59)
  • 正则表达式(常用字符简单版)

    量词 字符类 边界匹配 分组和捕获 特殊字符 字符匹配 普通字符:普通字符按照字面意义进行匹配,例如匹配字母 \\\"a\\\" 将匹配到文本中的 \\\"a\\\" 字符。 元字符:元字符具有特殊的含义,例如  d  匹配任意数字字符, w  匹配任意字母数字字符, .  匹配任意字符(除了换行符)

    2024年02月10日
    浏览(46)
  • 正则表达式简单易学,急速上手

    匹配模式 i:忽略大小写 g:全局匹配 ig:忽略大小写全局匹配 m:执行多行匹配 [a-e] :判断字符串中是否包含字母a-z 通过正则处理字符串 正则量词 正则表达式例子:

    2024年02月04日
    浏览(41)
  • C++ lambda表达式函数递归调用简单写法实现

    在C++11中,lambda表达式函数递归往往会带上 functional 头文件。书写形式如下: 还有相对简单点的第二种写法(C++14): 对于第二种, auto fib 的作用是为了在 lambda 表达式内部能够递归调用自身。在 C++14 中,lambda 表达式默认是无法直接递归调用的,因为在 lambda 内部无法访问到

    2024年02月15日
    浏览(51)
  • Cron表达式简单介绍 + Springboot定时任务的应用

    前言 表达式是一个字符串,主要分成6或7个域,但至少需要6个域组成,且每个域之间以空格符隔开。 以7个域组成的,从右往左是【年 星期 月份 日期 小时 分钟 秒钟】 秒 分 时 日 月 星期 年 以6个域组成的,从右往左是【星期 月份 日期 小时 分钟 秒钟】 秒 分 时 日 月 星

    2023年04月20日
    浏览(37)
  • LangChain 67 深入理解LangChain 表达式语言30 调用tools搜索引擎 LangChain Expression Language (LCEL)

    LangChain系列文章 LangChain 50 深入理解LangChain 表达式语言十三 自定义pipeline函数 LangChain Expression Language (LCEL) LangChain 51 深入理解LangChain 表达式语言十四 自动修复配置RunnableConfig LangChain Expression Language (LCEL) LangChain 52 深入理解LangChain 表达式语言十五 Bind runtime args绑定运行时参数

    2024年01月23日
    浏览(84)
  • 从零开始学习 Java:简单易懂的入门指南之正则表达式(十五)

    在Java中,我们经常需要验证一些字符串,例如:年龄必须是2位的数字、用户名必须是8位长度而且只能包含大小写字母、数字等。正则表达式就是用来验证各种字符串的规则。它内部描述了一些规则,我们可以验证用户输入的字符串是否匹配这个规则。 先看一个不使用正则表

    2024年02月12日
    浏览(42)
  • Django-带参数的路由编写(一)【不用正则表达式匹配的简单带参数路由】

    在某urls.py文件有如下的路由配置语句: 语句: 中的 int:id 就是带参数的URL中的参数部分,其语法格式如下: 参数数据类型:参数名称 URL参数有4种数据类型,如下表所示: 提问: Django的URL参数中的str类型和slug类型有什么区别?感觉都是对字符串的匹配啊! 答: 在Django中,

    2024年02月10日
    浏览(38)
  • 简单明了!网关Gateway路由配置filters实现路径重写及对应正则表达式的解析

    前端需要发送一个这样的请求,但出现404 首先解析请求的变化:  http://www.51xuecheng.cn/api/checkcode/pic 1.请求先打在nginx, www.51xuecheng.cn/api/checkcode/pic 部分匹配到了之后会转发给网关进行处理变成 localhost:63010/checkcode/pic  2.然后再转发到网关上,网关上的路由转发配置如下图。然

    2024年02月06日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包