文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-09-09 14:31

关注

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

引入依赖

<dependency>    <groupId>com.googlecode.aviatorgroupId>    <artifactId>aviatorartifactId>    <version>${version}version>dependency>

简单使用

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

import com.googlecode.aviator.AviatorEvaluator;import org.junit.jupiter.api.Test;@Testvoid 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;@Testvoid 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,并且这个公式可能在之后的场景里会改变,有了场景我们就来具体实现一下。

建表

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

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;@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;public interface AviatorExpressionService extends IService<AviatorExpression> {        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;@Service@RequiredArgsConstructorpublic 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 的三种用法:简单表达式变量表达式自定义函数
同时也通过一个简单的小案例供大家学习参考,大家也可以根据实际应用场景的不同来自行更改实现方式。

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


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

Bummon’s BlogBummon’s Home公众号

来源地址:https://blog.csdn.net/HackAzrael/article/details/131871172

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯